{"id":13621145,"url":"https://github.com/mrusme/journalist","last_synced_at":"2025-05-16T06:04:28.168Z","repository":{"id":37713269,"uuid":"328081331","full_name":"mrusme/journalist","owner":"mrusme","description":"Journalist. An RSS aggregator.","archived":false,"fork":false,"pushed_at":"2025-04-28T04:28:40.000Z","size":946,"stargazers_count":340,"open_issues_count":0,"forks_count":13,"subscribers_count":11,"default_branch":"master","last_synced_at":"2025-05-16T06:03:58.612Z","etag":null,"topics":["aggregator","api","atom","cloud-functions","digitalocean-app-platform","docker","ent","feed","fiber","heroku-deployment","journalist","lambda","mysql","news","postgresql","rss","rss-aggregator","rss-feed","sqlite3","terminal-based"],"latest_commit_sha":null,"homepage":"https://xn--gckvb8fzb.com/journalist-v1/","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mrusme.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null},"funding":{"custom":["https://github.com/mrusme#support"]}},"created_at":"2021-01-09T05:23:04.000Z","updated_at":"2025-05-15T21:17:11.000Z","dependencies_parsed_at":"2022-07-12T16:43:47.177Z","dependency_job_id":"7188e220-9d44-41f6-8887-cac57174d0cf","html_url":"https://github.com/mrusme/journalist","commit_stats":{"total_commits":229,"total_committers":6,"mean_commits":"38.166666666666664","dds":"0.46288209606986896","last_synced_commit":"dbb4e287c202d3422c6e8ef7b995dd53b2cfaedb"},"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mrusme%2Fjournalist","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mrusme%2Fjournalist/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mrusme%2Fjournalist/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mrusme%2Fjournalist/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mrusme","download_url":"https://codeload.github.com/mrusme/journalist/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254478186,"owners_count":22077675,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["aggregator","api","atom","cloud-functions","digitalocean-app-platform","docker","ent","feed","fiber","heroku-deployment","journalist","lambda","mysql","news","postgresql","rss","rss-aggregator","rss-feed","sqlite3","terminal-based"],"created_at":"2024-08-01T21:01:02.932Z","updated_at":"2025-05-16T06:04:28.100Z","avatar_url":"https://github.com/mrusme.png","language":"Go","funding_links":["https://github.com/mrusme#support"],"categories":["Go"],"sub_categories":[],"readme":"Journalist\n----------\n[![Tests](https://github.com/mrusme/journalist/actions/workflows/tests.yml/badge.svg)](https://github.com/mrusme/journalist/actions/workflows/tests.yml)\n[![Release](https://github.com/mrusme/journalist/actions/workflows/release.yml/badge.svg)](https://github.com/mrusme/journalist/releases)\n[![Docker](https://github.com/mrusme/journalist/actions/workflows/docker.yml/badge.svg)](https://hub.docker.com/r/mrusme/journalist)\n\n![journalist](journalist.png)\n\n[![Static \nBadge](https://img.shields.io/badge/Join_on_Matrix-green?style=for-the-badge\u0026logo=element\u0026logoColor=%23ffffff\u0026label=Chat\u0026labelColor=%23333\u0026color=%230DBD8B\u0026link=https%3A%2F%2Fmatrix.to%2F%23%2F%2521PHlbgZTdrhjkCJrfVY%253Amatrix.org)](https://matrix.to/#/%21PHlbgZTdrhjkCJrfVY%3Amatrix.org)\n\nJournalist. An RSS aggregator.\n\n\n## What is `journalist`?\n\nJournalist is an RSS aggregator that can sync subscriptions and read/unread\nitems across multiple clients without requiring a special client-side\nintegration. Clients can use Journalist by simply subscribing to its\npersonalized RSS feed.\n\nJournalist aims to become a self-hosted alternative to services like Feedly,\nFeedbin and others. It aims to offer a similar set of features like FreshRSS,\nNewsBlur and Miniflux while being easier to set up/maintain and overall more\nlightweight.\n\nFind out more about Journalist [here](https://xn--gckvb8fzb.com/journalist-v1/).\nIf you're looking for pre-v1.0.0 versions of Journalist, please check out the\n[v0 branch](https://github.com/mrusme/journalist/tree/v0). *v1.0.0 and later\nversions are not compatible to / upgradeable from pre-v1.0.0 versions!*\n\n\n## Usage\n\nJournalist is a single binary service can be run on any Linux/Unix machine\nby setting the required configuration values and launching the `journalist`\nprogram.\n\nBefore using Journalist from an RSS client, it first requires\n[configuration](#configuration) and [deployment](#deployment).\n\n### Getting Started\n\nAs soon as Journalist was [configured](#configuration) and\n[deployed](#deployment) properly, a new user can be added using the admin user\nthat Journalist creates automatically (default login: `admin`:`admin`).\n\nFirst, make sure to export `JOURNALIST_API_URL` in the current terminal session:\n\n```sh\n$ export JOURNALIST_API_URL=\"http://127.0.0.1:8000/api/v1\"\n```\n\nThen, using [Redacteur](#redacteur), a new user can be added like this:\n\n```sh\n$ JOURNALIST_API_USERNAME=admin JOURNALIST_API_PASSWORD=admin \\\n  ./redacteur add user\nUsername: johndoe\nPassword: MySecretPassword123\nRole (admin/[user]): user\n```\n\nNext, a new QAT (*Quick Access Token*) for the user can be issued:\n\n```sh\n$ JOURNALIST_API_USERNAME=johndoe JOURNALIST_API_PASSWORD=MySecretPassword123 \\\n  ./redacteur add token\nToken name: FeederAndroidClient\n```\n\nCopy the `token` from the JSON response, as this is required to subscribe to the\nJournalist feed.\n\nNext, add a new feed to the user (a.k.a. *subscribe to*):\n\n```sh\n$ JOURNALIST_API_USERNAME=johndoe JOURNALIST_API_PASSWORD=MySecretPassword123 \\\n  ./redacteur add feed\nURL: https://xn--gckvb8fzb.com\nName: マリウス\nGroup: Journals\n```\n\nFeel free to add further feeds the same way. `Group` describes a *folder*\nunderneath the feed should be grouped. Groups can be named freely.\n\nWith the *Quick Access Token* (`token`) that was copied previously, the\nfollowing URL can now be added to any RSS feed reader:\n\n```\nhttp://127.0.0.1:8000/web/subscriptions?qat=TOKEN-HERE\n```\n\nMore information and RSS feed URLs can be found under [Web](#web).\n\n\n## Configuration\n\nJournalist will read its config either from a file or from environment\nvariables. Every configuration key available in the\nexample [`journalist.toml`](examples/etc/journalist.toml) can be exported as\nenvironment variable, by separating scopes using `_` and prepend `JOURNALIST` to\nit. For example, the following configuration:\n\n```toml\n[Server]\nBindIP = \"0.0.0.0\"\n```\n\n... can also be specified as an environment variable:\n\n```sh\nexport JOURNALIST_SERVER_BINDIP=\"0.0.0.0\"\n```\n\nJournalist will try to read the `journalist.toml` file from one of the following\npaths:\n\n- `/etc/journalist.toml`\n- `$XDG_CONFIG_HOME/journalist.toml`\n- `$HOME/.config/journalist.toml`\n- `$HOME/journalist.toml`\n- `$PWD/journalist.toml`\n\n\n### Database\n\nJournalist requires a database to store users and subscriptions. Supported\ndatabase types are SQLite, PostgreSQL and MySQL. The database can be configured\nusing the `JOURNALIST_DATABASE_TYPE` and `JOURNALIST_DATABASE_CONNECTION` env,\nor the `Database.Type` and `Database.Connection` config properties.\n\n**WARNING:** If you do not specify a database configuration, Journalist will use\nan in-memory SQLite database! As soon as Journalist shuts down, all data\ninside the in-memory database is gone!\n\n\n#### SQLite File Example\n\n```toml\n[Database]\nType = \"sqlite3\"\nConnection = \"file:my-database.sqlite?cache=shared\u0026_fk=1\"\n```\n\n\n#### PostgreSQL Example *(using Docker for PostgreSQL)*\n\nRun the database:\n\n```sh\ndocker run -it --name postgres \\\n  -e POSTGRES_PASSWORD=postgres \\\n  -e POSTGRES_DB=journalist \\\n  -p 127.0.0.1:5432:5432 \\\n  -d postgres:alpine\n```\n\nConfigure `Database.Type` and `Database.Connection`:\n\n```toml\n[Database]\nType = \"postgres\"\nConnection = \"host=127.0.0.1 port=5432 dbname=journalist user=postgres password=postgres\"\n```\n\n\n#### MySQL Example\n\n```toml\n[Database]\nType = \"mysql\"\nConnection = \"mysqluser:mysqlpassword@tcp(mysqlhost:port)/database?parseTime=true\"\n```\n\n\n### Deployment\n\n#### Custom\n\nAll that's needed is a [configuration](#configuration) and Journalist can be\nlaunched by e.g. running `./journalist` in a terminal.\n\n\n#### Supervisor\n\nTo run Journalist via `supervisord`, create a config like this inside\n`/etc/supervisord.conf` or `/etc/supervisor/conf.d/journalist.conf`:\n\n```ini\n[program:journalist]\ncommand=/path/to/binary/of/journalist\nprocess_name=%(program_name)s\nnumprocs=1\ndirectory=/home/journalist\nautostart=true\nautorestart=unexpected\nstartsecs=10\nstartretries=3\nexitcodes=0\nstopsignal=TERM\nstopwaitsecs=10\nuser=journalist\nredirect_stderr=false\nstdout_logfile=/var/log/journalist.out.log\nstdout_logfile_maxbytes=1MB\nstdout_logfile_backups=10\nstdout_capture_maxbytes=1MB\nstdout_events_enabled=false\nstderr_logfile=/var/log/journalist.err.log\nstderr_logfile_maxbytes=1MB\nstderr_logfile_backups=10\nstderr_capture_maxbytes=1MB\nstderr_events_enabled=false\n```\n\n**Note:** It is advisable to run Journalist under its own, dedicated daemon\nuser (`journalist` in this example), so make sure to either adjust `directory`\nas well as `user` or create a user called `journalist`.\n\n\n#### OpenBSD rc\n\nAs before, create a configuration file under `/etc/journalist.toml`.\n\nThen copy the [example rc.d script](examples/etc/rc.d/journalist) to\n`/etc/rc.d/journalist` and copy the binary to e.g.\n`/usr/local/bin/journalist`. Last but not least, update the `/etc/rc.conf.local`\nfile to contain the following line:\n\n```conf\njournalist_user=\"_journalist\"\n```\n\nIt is advisable to run journalist as a dedicated user, hence create the\n`_journalist` daemon account or adjust the line above according to your setup.\n\nYou can now run Journalist by enabling and starting the service:\n\n```sh\nrcctl enable journalist\nrcctl start journalist\n```\n\n\n#### systemd\n\nTODO\n\n\n#### Docker\n\nOfficial images are available on Docker Hub at \n[mrusme/journalist](https://hub.docker.com/r/mrusme/journalist) \nand can be pulled using the following command:\n\n```sh\ndocker pull mrusme/journalist\n```\n\nGitHub release versions are available as Docker image tags (e.g. `1.0.0`). \nThe `latest` image tag contains the latest code of the `master` branch.\n\nIt's possible to build journalist locally as a Docker container like this:\n\n```sh\ndocker build -t journalist:latest . \n```\n\nIt can then be run using the following command:\n\n```sh\ndocker run -it --rm --name journalist \\\n  -e JOURNALIST_... \\\n  -e JOURNALIST_... \\\n  -p 0.0.0.0:8000:8000 \\\n  journalist:latest\n```\n\nAlternatively a configuration TOML can be passed into the container like so:\n\n```sh\ndocker run -it --rm --name journalist \\\n  -v /path/to/my/local/journalist.toml:/etc/journalist.toml \\\n  -p 0.0.0.0:8000:8000 \\\n  journalist:latest\n```\n\n\n#### Kubernetes\n\nTODO\n\n\n#### Render\n\nFork this repo into your GitHub account, adjust the\n[`render.yaml`](render.yaml) accordingly and connect the forked repo [on\nRender](https://dashboard.render.com/select-repo?type=blueprint).\n\nAlternatively, you can also directly connect this public repo.\n\n\n#### Heroku\n\n[![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy?template=https://github.com/mrusme/journalist)\n\n\n#### DigitalOcean App Platform\n\n[![Deploy to DO](https://www.deploytodo.com/do-btn-blue-ghost.svg)](https://cloud.digitalocean.com/apps/new?repo=https://github.com/mrusme/journalist/tree/master\u0026refcode=9d48825ddae1)\n  \nAlternatively, fork this repo into your GitHub account, adjust the\n[`.do/app.yaml`](.do/app.yaml) accordingly and connect the forked repo [on\nDigitalOcean](https://cloud.digitalocean.com/apps/new).\n\n\n#### DigitalOcean Function\n\nAvailable soon.\n\n\n#### Aamazon Web Services Lambda Function\n\nTODO\n\n\n#### Google Cloud Function\n\n```sh\ngcloud functions deploy GCFHandler --runtime go116 --trigger-http\n```\n\nTODO: Database\n\n\n## API\n\nJournalist provides an HTTP REST API for managing user accounts, tokens and\nfeeds, which is available through the `/api/v1` endpoint. A full OpenAPI/Swagger\ndocumentation of the API can be found inside the [`docs/`](docs/) folder.\n\n### Redacteur\n\nThis repository comes with a handy client for the Journalist API called\n[*Redacteur*](redacteur). Redacteur can be used to perform actions on the API,\neither by manually specifying the exact API request (`redacteur perform ...`) or\nby using a shorthand function like `create user`, which runs interactively.\n\nFind out more by running `redacteur help`.\n\n\n## Web\n\n`/web` is the HTTP web endpoint of Journalist that serves aggregated RSS feeds\nas well as *action* endpoints that allow for example marking items as read.\n\nTo subscribe to a Journalist user's aggregated RSS feed a *Quick Access Token*\nis required. It can be generated using [Redacteur](#redacteur).\n\nWith the `QAT`, any RSS feed reader can subscribe to the following URL:\n\n```\n\u003cJOURNALIST_SERVER_ENDPOINT_WEB\u003e/subscriptions?qat=\u003cTOKEN\u003e\n```\n\nAdditionally, subscriptions can be separated by *group*, simply by adding the\n`group` parameter to the URL:\n\n```\n\u003cJOURNALIST_SERVER_ENDPOINT_WEB\u003e/subscriptions?qat=\u003cTOKEN\u003e\u0026group=Journals\n```\n\nWith that, only feeds within the *Journal* group will be included in the RSS\nfeed.\n\n### Mark as Read\n\nFeed items can be marked as read using the inline Journalist menu that is\ninjected on the top of every RSS item. It contains a link to an *actions\nendpoint* of Journalist that will mark either a single item or a specific range\nof items as read. This will result in these items not showing up in the\nJournalist subscription feed anymore. This way every other client that will\neventually refresh the feed won't *see* these items anymore and hopefully not\ndisplay them.\n\nYou might need to adjust client settings in order to disable caching of items.\nAdditionally, if a client has previously synced the items, it might not\nautomatically remove them from the feed. Whether and how good this works depends\non the client's implementation.\n\n\n## Development\n\nFirst, install all required dependencies by running the following command in the\nrepository folder:\n\n```sh\nmake install-deps\n```\n\nYou can then build Journalist by running `make`:\n\n```sh\nmake\n```\n\nThis will build a binary called `journalist`.\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmrusme%2Fjournalist","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmrusme%2Fjournalist","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmrusme%2Fjournalist/lists"}