Expand description
The axum web layer — server-rendered HTML + a dash of htmx, no SPA.
This module owns the HTTP surface: router builds an axum::Router over
the shared AppState, wiring the store, feed, atproto, and config seams into
a small set of typography-first, dark-mode-ready views rendered with
askama templates (under templates/). Progressive enhancement is a single
vendored htmx script plus a tiny keyboard handler (static/keyboard.js);
every interaction also works as a plain HTML form POST, so the reader is fully
usable with JavaScript disabled.
§HTTP surface
GET /health— liveness + version, astext/plain.GET /— the reader: a folders/feeds sidebar (from the PDS records layer) plus the main article list. Query params pick the scope (?feed=…/?folder=…/ all) and the view (?view=unread|all|starred).GET /entries/{id}— the clean, distraction-free reader for one entry, with prev/next within the current list.POST /entries/{id}/read— mark an entry read/unread (htmx row swap).POST /entries/{id}/star— star/unstar; writes acommunity.lexicon.rss.savedrecord to the user’s PDS.POST /read-all— mark-all-read (per feed via?feed=…, else everything).POST /subscriptions— subscribe by URL (autodiscover → PDS record).POST /subscriptions/{rkey}/delete— unsubscribe (delete the PDS record).POST /subscriptions/{rkey}/rename— retitle / move a feed to a folder.POST /folders— create a folder record.POST /folders/{rkey}/rename— rename a folder record.POST /folders/{rkey}/delete— delete a folder record.POST /opml— OPML import (multipart upload or pasted textarea) → bulk subscription records in the PDS.GET /opml/export— OPML export (records → a downloadable document).GET /login+POST /login+/oauth/callback+/logout— the atproto OAuth sign-in flow (routed through the sidecar).
§Identity — a cookie-resolved atproto session
Per-request identity comes from a signed session cookie (fr_session)
keyed by the logged-in DID, set by [oauth_callback] and read by
[current_session] / [current_did]. For local runs without the sidecar,
Config::dev_did (env FEATHERREADER_DEV_DID) supplies a fallback identity.
All PDS writes route through the crate::atproto::SidecarClient; a live-PDS
write needs a real OAuth session, but the full write path is built and unit-
tested to the sidecar boundary.