A no-nonsense read later service
- β Add an article by making a
POSTrequest (withurlin aJSONbody) to/articles/add. - π Get a
JSONarray of all articles by making aGETrequest to/articles. - ποΈ Remove all articles by making a
DELETErequest to/articles/clear. - ποΈ All articles are stored in a
/data/local.sqliteSQLite database. - π₯ Get an RSS, Atom, and JSON feed of articles at
/rss,/atom, and/jsonrespectively.
Although you can clone/build readl8r locally, it's recommended for users to run the docker image on Docker Hub. Copy the contents of this docker-compose.yml file to your computer and run:
docker compose up| Name | Required | Description | Default |
|---|---|---|---|
AUTH_SECRET | If PASSWORD is set | Used to sign auth JWTs | undefined |
HOST | No | Hostname or IP address where the service is hosted | 0.0.0.0 |
PORT | No | The port number used for the service | 80 |
SECURE | No | Indicates whether to use HTTPS (true) or HTTP (false) | false |
PASSWORD | No | Password required for authentication | undefined |
FEED_TITLE | No | Title of the feed (displayed on the web app) | undefined |
FEED_DESCRIPTION | No | Brief description of the feed's content and purpose (displayed on the web app) | undefined |
FEED_IMAGE | No | URL to an image that represents the feed (e.g., logo or banner) | undefined |
FEED_FAVICON | No | URL to the favicon to be displayed in browsers for the feed | undefined |
FEED_COPYRIGHT | No | Copyright information regarding the content of the feed | undefined |
AUTHOR_NAME | No | Name of the feed's author | undefined |
AUTHOR_EMAIL | No | Email address of the author | undefined |
AUTHOR_LINK | No | URL to the author's website or social media profile | undefined |
You can optionally protect your reading list with a password by setting the PASSWORD and AUTH_SECRET environment variables in your docker compose config.
This will protect all routes excluding feed routes (/rss, /atom, etc).
I'm still looking into how rss aggregators generally handle auth for feeds and only want to add auth when it doesn't prevent aggregators from accessing reading lists.
{ id: number; url: string; publish_date: string; // date article was published (added_date if this can't be found) added_date: string; // date the article was added to readl8r title: string | null; description: string | null; content: string | null; author: string | null; favicon: string | null; ttr: number | null; // estimated time to read article in seconds }For the most up to date definition, see the actual typescript type.
π Requires Authentication
You can add an article by providing the article's url in the body of a POST request:
POST (http|https)://HOST:PORT/articles/add
| Status | Body | Content-Type |
|---|---|---|
| 200 | article added successfully | text/plain |
| 400 | url is required | text/plain |
| 400 | unable to extract metadata at {url} | text/plain |
| 401 | not authorized | text/plain |
π Requires Authentication
You can get a single JSON object representing an article by making a GET request to the /articles/:id route:
GET (http|https)://HOST:PORT/articles/:id
| Status | Body | Content-Type |
|---|---|---|
| 200 | Article | application/json |
| 401 | not authorized | text/plain |
| 404 | there is no article with an id of ":id" | text/plain |
π Requires Authentication
You can get a JSON array of articles by making a GET request to the /articles route:
GET (http|https)://HOST:PORT/articles
| Status | Body | Content-Type |
|---|---|---|
| 200 | Article[] | application/json |
| 401 | not authorized | text/plain |
π Requires Authentication
You can update an article based on it's id by making a PATCH request to the /articles/:id/update route:
PATCH (http|https)://HOST:PORT/articles/:id/update
{ "article": { "url": "", // optional "publish_date": "", // optional "added_date": "", // optional "title": "", // optional "description": "", // optional "content": "", // optional "author": "", // optional "favicon": "", // optional "ttr": "" // optional } }| Status | Body | Content-Type |
|---|---|---|
| 200 | article :id deleted successfully | text/plain |
| 401 | not authorized | text/plain |
| 404 | there is no article with id of :id | text/plain |
π Requires Authentication
You can delete an article based on it's id by making a DELETE request to the /articles/:id/delete route:
DELETE (http|https)://HOST:PORT/articles/:id/delete
| Status | Body | Content-Type |
|---|---|---|
| 200 | article :id deleted successfully | text/plain |
| 401 | not authorized | text/plain |
| 404 | there is no article with id of :id | text/plain |
π Requires Authentication
DELETE (http|https)://HOST:PORT/articles/clear
| Status | Body | Content-Type |
|---|---|---|
| 200 | x articles cleared successfully | text/plain |
| 401 | not authorized | text/plain |
π Requires Authentication
You can manually purge articles older than a certain threshhold using the /articles/purge route. Simply pass an older_than query parameter in the url with the following format:
h = hours d = days m = months y = years <integer>h|d|m|y Examples 30d = 30 days 4m = 4 months 2y = 2 years Please note the
older_thanparameter does not accept numbers with decimals.
DELETE (http|https)://HOST:PORT/articles/purge?older_than=<number>(h|d|m|y)
| Status | Body | Content-Type |
|---|---|---|
| 200 | x articles purged successfully | text/plain |
| 400 | invalid format, use the formula "<number><h | d | m | y>" | text/plain |
| 401 | not authorized | text/plain |
GET (http|https)://HOST:PORT/rss
GET (http|https)://HOST:PORT/rss.xml
GET (http|https)://HOST:PORT/feed
GET (http|https)://HOST:PORT/feed.xml
| Status | Body | Content-Type |
|---|---|---|
| 200 | RSS2 Feed | application/rss+xml |
GET (http|https)://HOST:PORT/atom
| Status | Body | Content-Type |
|---|---|---|
| 200 | Atom Feed | application/atom+xml |
GET (http|https)://HOST:PORT/json
| Status | Body | Content-Type |
|---|---|---|
| 200 | JSON Feed | application/json |
A simple GET route to see if the server is up and ready to handle incoming requests.
GET (http|https)://HOST:PORT/health
| Status | Body | Content-Type |
|---|---|---|
| 200 | OK | text/plain |
{ // required "url": "https://dev.to/jacobshuman/wtf-is-a-github-profile-readmemd-1p8c" }