Skip to content

murrple-1/rss_temple

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1,041 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

rss temple logo

RSS Temple

A fast, powerful, self-hostable RSS reader.


license-badge-img release-badge-img Docker CircleCI codecov

Table of Contents

Overview

RSS Temple is a fast, powerful, and self-hostable RSS/Atom reader, with a light, clean UI, and powerful subscription and search features.

It was original written as a personal attempt to reproduce the feeling of Google Reader, but with some of the features that I liked from Feedly, without needing to pay the subscription cost.

The official deployment of this project is at rsstemple.com, but I have no issue with this project being forked or redeployed.

My hope is that one day, this project be fully home-lab ready - so please, open a PR to help make this a reality.


It is made up of 3 projects - a landing page, an app frontend, and an app backend.

  • The landing page code can be found here.
  • The app frontend code can be found here.
  • The backend code can be found here.

Additionally, there is an Ansible collection (code available here, Ansible Galaxy deployment here) and a Terraform project (available here) which are intended to make deployment as easy as possible.

Features

  • Subscribe to any RSS or Atom feed
  • Can't find the feed URL? No problem, RSS Temple will find it for you
  • Fast, full-text search
  • Keyboard shortcuts

Preview

🖥 Desktop 📱 Mobile
desktop preview mobile preview
desktop preview dark mobile preview dark

Installation

Ansible (recommended)

Assuming you want to install everything on your current (local) machine:

# install the Ansible collection
ansible-galaxy collection install murrple_1.rss_temple
# sets up the shared infrastructure for the 3 projects
ansible-playbook --connection=local --inventory localhost, murrple_1.rss_temple.pre_rss_temple
# deploy the app backend
ansible-playbook --connection=local --inventory localhost, murrple_1.rss_temple.rss_temple
# deploy the app frontend
ansible-playbook --connection=local --inventory localhost, murrple_1.rss_temple.rss_temple_web_app
# deploy the landing page
ansible-playbook --connection=local --inventory localhost, murrple_1.rss_temple.rss_temple_home

Manual

RSS Temple Infrastructure

Ensure Docker is installed.

# create the shared Docker network
docker network create global_rss_temple_net

Create a directory /opt/rss_temple/rss_temple_infra/.

In the new directory, create the following files - editing them as needed:

/opt/rss_temple/rss_temple_infra/docker-compose.yml

services:
  caddy:
    image: caddy:2-alpine
    restart: always
    ports:
      - '80:80'
      - '443:443'
    volumes:
      - caddy_data:/data/
      - ./Caddyfile:/etc/caddy/Caddyfile
    networks:
      - default
      - global_rss_temple_net
volumes:
  caddy_data:
networks:
  global_rss_temple_net:
    external: true

/opt/rss_temple/rss_temple_infra/Caddyfile

https://api.rsstemple.com {
  log

  encode gzip

  reverse_proxy caddy-rss_temple:8000
}

http://api.rsstemple.com {
  respond "The API is only accessible over HTTPS" 403 {
    close
  }
}

app.rsstemple.com {
  log

  encode gzip

  reverse_proxy caddy-rss_temple_web_app:4200
}

rsstemple.com {
  log

  encode gzip

  reverse_proxy caddy-rss_temple_home:3000
}

Replace rsstemple.com with your top-level domain.

Start the containers with docker compose up -d.

RSS Temple Backend

Create a directory /opt/rss_temple/rss_temple/, and also /opt/rss_temple/rss_temple/overrides/ and /opt/rss_temple/rss_temple/mount/.

Create the following files - editing them as needed:

/opt/rss_temple/rss_temple/docker-compose.yml

x-rss-temple-image: &rss-temple-image
  image: 'murraychristopherson/rss_temple:latest'

services:
  valkey:
    image: valkey/valkey:8-alpine
    command: valkey-server /usr/local/etc/valkey/valkey.conf
    restart: always
    volumes:
      - ./overrides/valkey.conf:/usr/local/etc/valkey/valkey.conf
    networks:
      - rss_temple_net
  postgresql:
    image: postgres:18-alpine
    restart: always
    shm_size: '256m'
    expose:
      - '5432'
    env_file:
      - .env
    volumes:
      - db_data:/var/lib/postgresql/data/
    networks:
      - rss_temple_net
  caddy-rss_temple:
    image: caddy:2-alpine
    restart: always
    expose:
      - '8000'
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile
      - caddy_data:/data/
      - django_static:/srv/
    networks:
      - rss_temple_net
      - global_rss_temple_net
  rss_temple:
    <<: *rss-temple-image
    command: >
      sh -c "
        while ! python ./manage.py checkready; do
          sleep 0.1
        done

        python ./manage.py collectstatic --noinput &

        python ./manage.py migrate

        exec gunicorn \\
          -b 0.0.0.0:8000 \\
          rss_temple.wsgi:application
      "
    restart: always
    environment:
      APP_IN_DOCKER: 'true'
      MALLOC_CONF: background_thread:true,max_background_threads:1,metadata_thp:auto,dirty_decay_ms:30000,muzzy_decay_ms:30000
      #APP_DEBUG: 'true'
      #APP_DJANGO_DB_BACKEND_LOG_LEVEL: DEBUG
    env_file:
      - .env
    volumes:
      - ./overrides/local_settings.py:/code/rss_temple/local_settings.py
      - ./overrides/gunicorn.conf.py:/code/gunicorn.conf.py
      - ./mount/:/code/mount/
      - django_static:/code/_static/
    networks:
      - default  # necessary, because functionality requires making calls to the external internet
      - rss_temple_net
  rss_temple_dramatiq:
    <<: *rss-temple-image
    command: dramatiq api_dramatiq.main -Q rss_temple
    restart: always
    environment:
      APP_IN_DOCKER: 'true'
      MALLOC_CONF: background_thread:true,max_background_threads:1,metadata_thp:auto,dirty_decay_ms:30000,muzzy_decay_ms:30000
    env_file:
      - .env
    volumes:
      - ./overrides/local_settings.py:/code/rss_temple/local_settings.py
      - ./mount/:/code/mount/
    networks:
      - default  # necessary, because functionality requires making calls to the external internet
      - rss_temple_net
  rss_temple_schedulerdaemon:
    <<: *rss-temple-image
    command: >
      sh -c "
        while ! python ./manage.py checkready; do
          sleep 0.1
        done

        while ! python ./manage.py migrate --check; do
          sleep 5
        done

        python ./manage.py checkclassifierlabels

        exec python ./manage.py schedulerdaemon /schedulerdaemon.json
      "
    restart: always
    environment:
      APP_IN_DOCKER: 'true'
      MALLOC_CONF: background_thread:true,max_background_threads:1,metadata_thp:auto,dirty_decay_ms:30000,muzzy_decay_ms:30000
    env_file:
      - .env
    volumes:
      - ./schedulerdaemon.json:/schedulerdaemon.json
      - ./overrides/local_settings.py:/code/rss_temple/local_settings.py
      - ./mount/:/code/mount/
    networks:
      - rss_temple_net
volumes:
  db_data:
  caddy_data:
  django_static:
networks:
  global_rss_temple_net:
    external: true
  rss_temple_net:
    internal: true

/opt/rss_temple/rss_temple/.env

TZ=UTC
DJANGO_SETTINGS_MODULE=rss_temple.settings

POSTGRES_PASSWORD='CHANGE_ME_POSTGRES_PASSWORD'

APP_DEBUG=False

APP_DB_HOST=postgresql
APP_DB_USER=postgres
APP_DB_PASSWORD='CHANGE_ME_POSTGRES_PASSWORD'

APP_REDIS_URL=redis://valkey:6379

APP_SECRET_KEY='CHANGE_ME_SECRET_KEY'

APP_SESSION_COOKIE_DOMAIN=.rsstemple.com
APP_CSRF_TRUSTED_ORIGINS=https://api.rsstemple.com,https://app.rsstemple.com
APP_CSRF_COOKIE_DOMAIN=.rsstemple.com

APP_EMAIL_HOST=
APP_EMAIL_PORT=587
APP_EMAIL_HOST_USER=
APP_EMAIL_HOST_PASSWORD=
APP_EMAIL_USE_TLS=True
APP_EMAIL_USE_SSL=False
APP_EMAIL_TIMEOUT=10.0
APP_DEFAULT_FROM_EMAIL=

APP_ACCOUNT_CONFIRM_EMAIL_URL='https://app.rsstemple.com/verify?token=%(key)s'
APP_ACCOUNT_EMAIL_VERIFICATION_SENT_URL=https://app.rsstemple.com/emailsent
APP_PASSWORD_RESET_CONFIRM_URL_FORMAT='https://app.rsstemple.com/resetpassword?token=%(token)s&userId=%(userId)s'
APP_SOCIAL_CONNECTIONS_URL=https://app.rsstemple.com/main/profile
APP_SOCIAL_SIGNUP_URL=https://app.rsstemple.com/register

Replace rsstemple.com with your top-level domain.

Generate, or otherwise replace, the passwords/secret keys (values start with 'CHANGE_ME_') with secure alternatives.

TODO: I should integrate some secret management.

You'll want to setup the APP_EMAIL_* variables, to setup email verification.

/opt/rss_temple/rss_temple/schedulerdaemon.json

{
    "archive_feed_entries": {},
    "delete_old_job_executions": {},
    "extract_top_images": {},
    "feed_scrape": {},
    "flag_duplicate_feeds": {},
    "label_feeds": {},
    "label_users": {},
    "purge_duplicate_feed_urls": {},
    "purge_expired_data": {},
    "setup_subscriptions": {},
    "ignore_missed_top_images": {}
}

/opt/rss_temple/rss_temple/Caddyfile

:8000 {
        encode gzip

        @nocache {
                path *.manifest *.appcache *.html *.xml *.json
        }
        header @nocache {
                ?Cache-Control "no-cache"
        }

        @yearcache {
                path *.css *.js
        }
        header @yearcache {
                ?Cache-Control "max-age=31536000"
        }

        handle_path /static* {
                root * /srv
                file_server
        }

        @web {
                path *
        }
        reverse_proxy @web rss_temple:8000
}

/opt/rss_temple/rss_temple/overrides/local_settings.py

import warnings

from html5lib.constants import DataLossWarning

warnings.simplefilter("ignore", category=DataLossWarning)

/opt/rss_temple/rss_temple/overrides/gunicorn.conf.py

worker_class = "gthread"
capture_output = True
accesslog = "-"

/opt/rss_temple/rss_temple/overrides/valkey.conf

maxmemory 0
maxmemory-policy noeviction

Start the containers with docker compose up -d.

RSS Temple Frontend

Create a directory /opt/rss_temple/rss_temple_web_app/, and also /opt/rss_temple/rss_temple_web_app/custom_html/.

In the new directory, create the following files - editing them as needed:

/opt/rss_temple/rss_temple_web_app/docker-compose.yml

x-rss-temple-web-app-image: &rss-temple-web-app-image
  image: 'murraychristopherson/rss_temple_web_app:latest'

services:
  caddy-rss_temple_web_app:
    <<: *rss-temple-web-app-image
    command: >
      ash -c "
        if [ -e /custom_html/index.head.html ]; then
          echo 'customizing /srv/index.html...'
          sed -i -e \"/<!-- DEPLOYMENT HEAD START -->/,/<!-- DEPLOYMENT HEAD END -->/{/<!-- DEPLOYMENT HEAD START -->/{r /custom_html/index.head.html
      p};/<!-- DEPLOYMENT HEAD END -->/!d}\" /srv/index.html
          echo 'done'
        fi
        exec caddy run --config /etc/caddy/Caddyfile --adapter caddyfile
      "
    restart: always
    expose:
      - '4200'
    volumes:
      - caddy_data:/data/
      - caddy_config:/config/
      - ./Caddyfile:/etc/caddy/Caddyfile
      - ./config.json:/srv/assets/config.json
      - ./custom_html/:/custom_html/
    networks:
      - rss_temple_web_app_net
      - global_rss_temple_net
volumes:
  caddy_data:
  caddy_config:
networks:
  global_rss_temple_net:
    external: true
  rss_temple_web_app_net:
    internal: true

/opt/rss_temple/rss_temple_web_app/Caddyfile

:4200 {
  header {
    X-Content-Type-Options "nosniff"
    X-Frame-Options "DENY"
    Content-Security-Policy "default-src 'none'; script-src 'self' apis.google.com connect.facebook.net; connect-src 'self' *.rsstemple.com www.facebook.com; img-src * data: blob:; style-src 'self' 'unsafe-inline'; base-uri 'self'; form-action 'self'; font-src 'self' data:; frame-src *; media-src blob:"
    X-XSS-Protection "1"
    Strict-Transport-Security "max-age=31536000; includeSubDomains"
  }

  root * /srv/
  file_server
  try_files {path} /index.html
}

/opt/rss_temple/rss_temple_web_app/config.json

{
  "apiHost": "https://api.rsstemple.com",
  "clientRepoUrl": "https://github.com/murrple-1/rss_temple_ui",
  "facebookAppId": "CHANGE_ME_FACEBOOK_APP_ID",
  "googleClientId": "CHANGE_ME_GOOGLE_CLIENT_ID",
  "issueTrackerUrl": "https://github.com/murrple-1/rss_temple/issues",
  "onboardingYoutubeEmbededUrl": "https://www.youtube.com/embed/dQw4w9WgXcQ",
  "serverRepoUrl": "https://github.com/murrple-1/rss_temple",
  "privacyPolicyUrl": "https://rsstemple.com/privacy",
  "tosUrl": "https://rsstemple.com/tos",
  "forceLabelThreshold": 0.5,
  "extraNavLinks": [],
  "donationBadges": [],
  "lemmyInstanceSuggestions": [
    "lemmy.world",
    "lemmy.ml",
    "lemm.ee",
    "lemmy.zip"
  ],
  "mastodonInstanceSuggestions": ["mastodon.social", "mastodon.online"]
}

Replace rsstemple.com with your top-level domain.

Replace the app IDs (values start with 'CHANGE_ME_') with your own IDs (this allows for SSO login).

/opt/rss_temple/rss_temple_web_app/custom_html/index.head.html

<meta property="og:title" content="RSS Temple App" />
<meta property="og:image" content="https://app.rsstemple.com/assets/images/og_image.jpg" />
<meta property="og:url" content="https://app.rsstemple.com" />
<meta property="og:type" content="website" />
<meta property="og:description" content="The Zen of Syndication" />

Replace rsstemple.com with your top-level domain.

Start the containers with docker compose up -d.

RSS Temple Landing Page

Create a directory /opt/rss_temple/rss_temple_home/, and also /opt/rss_temple/rss_temple_home/custom_html/.

In the new directory, create the following files - editing them as needed:

/opt/rss_temple/rss_temple_home/docker-compose.yml

x-rss-temple-home-image: &rss-temple-home-image
  image: 'murraychristopherson/rss_temple_home:latest'

services:
  caddy-rss_temple_home:
    image: caddy:2-alpine
    restart: always
    expose:
      - '3000'
    volumes:
      - caddy_data:/data/
      - caddy_config:/config/
      - caddy_srv:/srv/
      - ./Caddyfile:/etc/caddy/Caddyfile
    networks:
      - rss_temple_home_net
      - global_rss_temple_net
  rss_temple_home:
    <<: *rss-temple-home-image
    command: >
      ash -c "
        [ \"$$(ls -A /srv/)\" ] || echo '<html><body><h1>Building Site...</h1></body></html>' > /srv/index.html
        yarn build

        rm -rf /srv/* && cp -R /build/_site/* /srv/

        while :; do
          sleep 3600
        done
      "
    restart: always
    env_file:
      - .env
    environment:
      CUSTOM_HTML_DIR: '/custom_html/'
    volumes:
      - caddy_srv:/srv/
      - ./custom_html/:/custom_html/
    networks:
      - rss_temple_home_net
volumes:
  caddy_data:
  caddy_config:
  caddy_srv:
networks:
  global_rss_temple_net:
    external: true
  rss_temple_home_net:
    internal: true

/opt/rss_temple/rss_temple_home/.env

WEB_APP_URL=http://app.rsstemple.com
TWITTER_X_URL=https://x.com/rsstemple
FB_URL=https://www.facebook.com/RSSTemple/
INSTA_URL=https://www.instagram.com/rsstempleapp/

Point these at your own socials, if you'd like.

/opt/rss_temple/rss_temple_home/Caddyfile

:3000 {
  header {
    X-Content-Type-Options "nosniff"
    X-Frame-Options "DENY"
    Content-Security-Policy "default-src 'none'; script-src 'self'; connect-src 'self'; img-src 'self'; style-src 'self' 'unsafe-inline' fonts.googleapis.com; base-uri 'self'; form-action 'self'; font-src 'self' fonts.gstatic.com"
    X-XSS-Protection "1"
    Strict-Transport-Security "max-age=31536000; includeSubDomains"
  }

  root * /srv/
  file_server
}

The following files are custom HTML injected into each page. You can arguably put any HTML you want, but the examples below are a good start. Switch out rsstemple.com for whatever your top-level domain is.

/opt/rss_temple/rss_temple_home/custom_html/index.head.html

<meta property="og:title" content="RSS Temple" />
<meta property="og:image" content="https://rsstemple.com/media/og_image.jpg" />
<meta property="og:url" content="https://rsstemple.com" />
<meta property="og:type" content="website" />
<meta property="og:description" content="The Zen of Syndication" />

/opt/rss_temple/rss_temple_home/custom_html/contact.html

<section class="app--legal">
  <h2>Email</h2>
  <p>
    <a href="mailto:test@test.com">test@test.com</a>
  </p>
</section>

/opt/rss_temple/rss_temple_home/custom_html/contact.head.html

<meta property="og:title" content="RSS Temple" />
<meta property="og:image" content="https://rsstemple.com/media/og_image.jpg" />
<meta property="og:url" content="https://rsstemple.com" />
<meta property="og:type" content="website" />
<meta property="og:description" content="The Zen of Syndication" />

/opt/rss_temple/rss_temple_home/custom_html/privacy.html

<section class="app--legal">
  <p>Last Revised: <strong>[ DATE ]</strong></p>
  <h2>Your Privacy Policy</h2>
  <!-- fill this out however you like -->
</section>

/opt/rss_temple/rss_temple_home/custom_html/privacy.head.html

<meta property="og:title" content="RSS Temple" />
<meta property="og:image" content="https://rsstemple.com/media/og_image.jpg" />
<meta property="og:url" content="https://rsstemple.com" />
<meta property="og:type" content="website" />
<meta property="og:description" content="The Zen of Syndication" />

/opt/rss_temple/rss_temple_home/custom_html/tos.html

<section class="app--legal">
  <p>Last Revised: <strong>[ DATE ]</strong></p>
  <h2>Your Terms of Service</h2>
  <!-- fill this out however you like -->
</section>

/opt/rss_temple/rss_temple_home/custom_html/tos.head.html

<meta property="og:title" content="RSS Temple" />
<meta property="og:image" content="https://rsstemple.com/media/og_image.jpg" />
<meta property="og:url" content="https://rsstemple.com" />
<meta property="og:type" content="website" />
<meta property="og:description" content="The Zen of Syndication" />

Technical Support

If you have any issues with RSS Temple, please open an issue in this repository.

Project Support

Consider supporting the development of this project on Ko-Fi. All funds will be used to cover the costs of hosting, development, and maintenance of RSS Temple.

Ko-Fi

About

Self-hostable, fast, simple RSS/Atom reader - Backend

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages