Add read-only web UI

Serves Pete's classified-story archive over HTTP alongside the Matrix
bot. Three sections (gaming/tech/politics), Animal-Crossing-vibe Tailwind
templates, day/night palette driven by the visitor's browser clock.
Web port configurable via web.listen_addr and ${PETE_WEB_PORT} in
docker-compose. Tailwind built in a node stage in the Dockerfile so
deployments don't need node at runtime.
This commit is contained in:
prosolis
2026-05-24 21:51:49 -07:00
parent 87906719fa
commit fbd4b84eaf
20 changed files with 1695 additions and 1 deletions

View File

@@ -15,6 +15,7 @@ A Matrix news bot that ingests RSS feeds from curated sources, classifies storie
- **Paywall detection** — if an article's visible body text is below threshold, Pete swaps in a Wayback Machine snapshot URL for both the lead image and the posted link
- **FTS5 search** — full-text search across headlines and ledes
- **Image validation** — HEAD-based checks filter tracking pixels, uploads valid images via MXC
- **Web UI** — read-only browser interface (Tailwind, Animal-Crossing-ish vibe, day/night palette that follows the visitor's clock) at `/`, `/gaming`, `/tech`, `/politics`. Designed for a `news.parodia.dev`-style deployment
## Channels
@@ -62,9 +63,27 @@ See `config.example.yaml` for the full structure. Key sections:
- `posting` — rate limiting (min interval, burst cap, daily cap), optional `round_robin` block
- `storage` — database path, retention windows
- `sources` — RSS feeds with tier, polling interval, feed hint, optional direct routing
- `web` — read-only HTTP UI (enabled toggle, listen address, site title, public base URL)
Environment variables can be referenced with `${VAR}` syntax in the YAML.
### Web UI
Set `web.enabled: true` (default port `:8080`) to expose Pete's classified-story archive over HTTP. Three sections (`/gaming`, `/tech`, `/politics`) plus a landing page at `/`, all pulling from the `stories` table. Adding a section is two lines in `internal/web/server.go` (the `channels` slice) plus a matching theme color in `internal/web/static/css/input.css`.
The frontend uses a small Tailwind build:
```bash
npm install
npm run build:css # produces internal/web/static/css/output.css
# or for live editing:
npm run watch:css
```
The Docker image runs the build automatically as a first stage, so deployments don't need Node.
Front the server with a reverse proxy (Caddy, nginx, Traefik) terminating TLS for e.g. `news.parodia.dev → 127.0.0.1:8080`. The day/night palette is driven by the visitor's browser clock — no server-side timezone needed.
### Round-robin mode
Set `posting.round_robin.enabled: true` to switch Pete from "post on classify" to a paced rotation. On each tick (`interval_hours`, default 4) Pete picks the newest unposted classified story routed to the next channel in sorted order, posting through the existing queue. Empty channels are skipped; the rotation pointer advances to whichever channel actually posted, and `last_channel` / `last_tick_at` are persisted so restarts don't reset the cycle. Rotating by channel (not by source) guarantees variety even when one channel has many more feeds than the others.
@@ -113,6 +132,7 @@ Posted message ← ❓ reaction → Explainer → Article Fetch → Ollama summa
| `internal/poster` | Per-channel metered release queue, reaction tracking, callback hook |
| `internal/explainer` | ❓-reaction → article fetch → Ollama summary → threaded reply |
| `internal/scheduler` | Round-robin posting scheduler: paced rotation across channels when enabled |
| `internal/web` | Read-only HTTP UI: Tailwind templates, day/night palette, per-channel feed pages |
## Post Format