{"id":24289275,"url":"https://github.com/rla/feeds","last_synced_at":"2025-09-10T17:47:50.714Z","repository":{"id":8920986,"uuid":"10648903","full_name":"rla/feeds","owner":"rla","description":"Personal web-based replacement for Google Reader and other feed readers.","archived":false,"fork":false,"pushed_at":"2023-01-01T03:51:21.000Z","size":2618,"stargazers_count":3,"open_issues_count":25,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-01-16T10:58:32.881Z","etag":null,"topics":["feed-reader","nodejs"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rla.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2013-06-12T18:18:08.000Z","updated_at":"2023-06-19T00:31:52.000Z","dependencies_parsed_at":"2023-01-11T17:30:26.889Z","dependency_job_id":null,"html_url":"https://github.com/rla/feeds","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rla%2Ffeeds","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rla%2Ffeeds/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rla%2Ffeeds/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rla%2Ffeeds/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rla","download_url":"https://codeload.github.com/rla/feeds/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":242128906,"owners_count":20076276,"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":["feed-reader","nodejs"],"created_at":"2025-01-16T10:51:46.313Z","updated_at":"2025-03-06T01:22:13.593Z","avatar_url":"https://github.com/rla.png","language":"TypeScript","readme":"# Feeds\n\nMy personal web-based replacement for Google Reader (was shutdown) and\nother feed readers. It tracks seen/read/important articles and is built for\nspeed. I currently follow about 600 feeds and other readers had trouble\ncoping with this amount of feeds.\n\n## How does it work\n\nThe feed reader works differently to other similar applications. It only displays\nitem titles and assumes that an item title provides enough information to decide\nwhether you want or want not to read it. The actual article is displayed in a seperate\nbrowser tab, in the case you decide to read it.\n\nThe main view of the application displays the list of \"unseen\" articles. These are\nthe fresh items from recently updated newsfeeds. Clicking \"Seen\" on an item will mark\nit, and the previous items, as \"seen\". This allows to skip a large number of\nuninteresting articles. This is what makes this app to scale to a very high number\nof feeds without having too much cognitive overhead to mark things \"read\" manually\nor have UI slow down to useless speed because you have too many feeds and articles.\n\nThe items appear in the \"unseen\" list by the order of publication date. The list\nuses infinite scroll.\n\nRead articles are also tracked. An article can be marked as \"important\" which is\ndisplayed in the list of \"important\" articles.\n\nScreenshot:\n\n![Screenshot](etc/feeds.png)\n\n## How is it built\n\nThe application runs on NodeJS. Feeds are parsed with [fast-feed](https://github.com/rla/fast-feed)\nwhich uses [RapidXML](http://rapidxml.sourceforge.net/) internally to parse the feed and extract\nthe interesting data.\n\nThe user interface uses a (custom) [Bootstrap](http://twitter.github.io/bootstrap/) stylesheet. I only\nselected the parts of Bootstrap that I needed. SQLite is used as the database on the server side.\n\nSince 2017-07-01, the user interface has been rewritten in React. There is no difference\nin functionality compared to the previous versions, however. The previous user interface\nwas developed with Knockout.js.\n\nMy live app is running at [http://feeds.rlaanemets.com/](http://feeds.rlaanemets.com/).\n\n## Installing\n\n1.  First install dependencies using `npm install --production`.\n2.  Then create database using `make db.sqlite`.\n3.  Then copy `config.example.json` to `config.json`.\n4.  Run app with `NODE_ENV=production node dist`.\n\n## Importing feed addresses\n\nUse the textarea on top of the \"Feeds\" list to enter a list of urls.\nInvalid urls and feeds will later be shown under \"Invalid\". The urls have to point\ndirectly to newsfeeds. The newsfeed urls are currently not extracted from\nsite urls!\n\n## Technology\n\nThe project has become my personal playground to test various frontent (mostly) libraries.\nThe whole project is type-checked and compiled by TypeScript. This has made it easier to\nrefactor although it causes some friction with libraries that have no types available or\nare very dynamic to have useful types.\n\n### Architecture\n\nThe application is built as a Single Page Application (SPA) with a server-side\nbackend. The backend manages the database, fetches newsfeeds periodically, and\nprovides a REST API for the frontend.\n\nBackend:\n\n- [Express][express] web framework.\n- SQLite database.\n  - Uses package `node-sqlite` for Promise-based access.\n  - Custom transaction manager in `src/lib/db`.\n- [Fast-feed][fast-feed] RSS/Atom parser.\n  - This package was developed for this application although\n    it has been used by others too.\n\n[express]: https://expressjs.com/\n[fast-feed]: https://github.com/rla/fast-feed\n\nBackend files:\n\n- `src` - source tree (TypeScript).\n- `src/index.ts` - application entrypoint.\n- `Makefile` - helper to create an initial database state.\n- `schema/schema.sql` - database schema.\n- `schema/migrations` - migrations applied after the initial schema.\n\nFrontend:\n\n- React view library.\n- Redux state store.\n  - Uses package `redux-thunk` for async actions.\n  - Typesafe actions, reducers, and state with TypeScript.\n- Typesafe REST API with shared types between frontend and backend.\n- Compiled and bundled through Webpack.\n  - Separated external libraries bundle.\n- UI elements use Bootstrap default styles.\n\nFrontend files:\n\n- `public/js/app` - SPA source (TypeScript).\n\n### Code style\n\nSource code style is checked by [TSLint][tslint] with minimal\ncustomization of the default rules.\n\n[tslint]: https://palantir.github.io/tslint/\n\n### Building\n\nBuild backend:\n\n```\nnpm run backend-compile\n```\n\nThis creates `dist` directory. The backend code is ran by running:\n\n```\nnode dist\n```\n\nBuild frontend (production bundle):\n\n```\nnpm run frontend-compile-production\n```\n\nor (development bundle in watch mode):\n\n```\nnpm run frontend-compile-development\n```\n\nThis will create either files `X.production.bundle.js` or `X.development.bundle.js`.\nThe right file is selected by the bootstrapping HTML view which uses NODE_ENV\nenvironment variable provided to the backend node process. The production bundle is\nchecked into git.\n\n### Unit testing\n\nUnit testing for frontend is implemented using [jest][jest].\n\n[jest]: https://jestjs.io/\n\nRunning frontend tests:\n\n```sh\nnpm run frontend-test\n```\n\nRunning backend tests:\n\n```sh\nnpm run backend-test\n```\n\nWhat is tested?\n\n- Redux reducers. It is checked that the state is correctly\n  transformed according to the dispatched actions.\n- Redux actions including async actions. Api is mocked and\n  actions are checked to create correct actions.\n- Shallow rendering of React components to catch potential crashes.\n- Some backend functionality is unit-tested.\n- More cases can be easily added to the existing tests.\n\n### Integration/E2E testing\n\nThere are 2 integration tests: running newsfeed updates from the command\nline and full stack test with [Puppeteer][puppeteer]. Tests are executes using Jest\nagain, except that they are run serially to save resources and avoid\npossible interleaving issues from concurrency.\n\n[puppeteer]: https://github.com/GoogleChrome/puppeteer\n\n- Newsfeed update test: `testing/fetcher.test.js`.\n- Full stack integration test: `testing/app.test.js`.\n\nBoth tests run fully compiled application. To compile:\n\n```\nnpm run backend-compile\nnpm run frontend-compile\n```\n\nAnd run tests:\n\n```\nnpm run integration-test\n```\n\nIf whole stack test gives an error then the app can be started with test\ndata using:\n\n```\nnode dist -c testing/app.config.json\n```\n\n### Toolset configuration\n\nThe project contains the following toolset configuration files (as\na future reference for a similar project):\n\n- `package.json` - standard NPM configuration for the project.\n- `jest.backend.config.js` - Jest configuration for the backend tests.\n- `jest.frontend.config.js` - Jest configuration for the frontend tests. Differs\n  by the TypeScript configuration file locations and some plugins.\n- `jest.integration.config.js` - Jest configuration for the integration tests. Differs\n  by the TypeScript configuration file locations.\n- `tslint.json` - TSLint configuration.\n- `src/tsconfig.json` - TypeScript configuration for the backend.\n- `public/js/app/tsconfig.json` - TypeScript configuration for the frontend. Has support\n  for JSX.\n- `webpack.config.js` - Webpack configuration for the frontend.\n\n### Debugging\n\nDebugging backend:\n\nTo start fetching of feeds immediately, start the app with `-f` switch:\n\n```\nnode dist -f\n```\n\nThe backend uses source maps and `source-map-support` to map error stack\ntraces to the TypeScript source code.\n\nTo see debug messages from the backend (it uses the `debug` package):\n\n```\nDEBUG=app:*\n```\n\nDebugging frontend:\n\nBoth JavaScript and CSS use source maps, making it possible to set breakpoints\nand trace associated code back to its original source.\n\nFrontend JavaScript sets up support for [Redux Devtools][redux-devtools]. For debugging\nReact components, [React Developer Tools][react-devtools] is a very useful browser extension.\n\n[redux-devtools]: https://github.com/reduxjs/redux-devtools\n[react-devtools]: https://github.com/facebook/react-devtools\n\n## Changelog\n\n- 2019-08-25: Support disabling read access to anonymous users. Code reformatted with Prettier.\n- 2019-01-24: Integration tests.\n- 2019-01-22: Port to TypeScript + Redux. Unit tests.\n- 2017-07-01: UI rewrite to React.\n- 2016-12-30: Upgrading to version 0.2.0 requires a migration, run with:\n  sqlite3 db.sqlite \u003c schema/migrations/004_add_article_rowid.sql\n\n## Misc\n\nApp's favicon is from [favicon.cc](http://www.favicon.cc/?action=icon\u0026file_id=360427), made by Jason.\n\n## License\n\nThe MIT License.\n\n```\nCopyright (c) 2013-2019 Raivo Laanemets\n\nPermission is hereby granted, free of charge, to any person\nobtaining a copy of this software and associated documentation\nfiles (the \"Software\"), to deal in the Software without restriction,\nincluding without limitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of the Software,\nand to permit persons to whom the Software is furnished to do so,\nsubject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included\nin all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS\nOR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\nTHE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\nFROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\nIN THE SOFTWARE.\n```\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frla%2Ffeeds","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frla%2Ffeeds","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frla%2Ffeeds/lists"}