news-digest/README: document multi-tenant onboarding + per-user cron
Adds a "Customizing the run schedule" section (DIGEST_CRON_AM/PM env vars, edit-and-recreate flow) and a "Multi-tenant: one instance per teammate" section covering scripts/add-digest-user.sh end to end: what it does, the per-user file layout on ana-docker, idempotent schedule/password updates, and the teardown path. Updated the stale "two editions per day" intro line to note the schedule is now configurable.
This commit is contained in:
@@ -27,8 +27,10 @@ noisy. This stack:
|
|||||||
(state in `/output/hidden.json`, shared across every device the
|
(state in `/output/hidden.json`, shared across every device the
|
||||||
user opens the digest from).
|
user opens the digest from).
|
||||||
|
|
||||||
Two editions per day: 8am and 8pm local. Plus per-edition archives
|
Two editions per day by default (0800 / 2000 local), parametrized via
|
||||||
at `/edition-YYYY-MM-DD-{am,pm}.html`.
|
`DIGEST_CRON_AM` / `DIGEST_CRON_PM` env so each per-user instance can
|
||||||
|
fire on its own schedule. Plus per-edition archives at
|
||||||
|
`/edition-YYYY-MM-DD-{am,pm}.html`.
|
||||||
|
|
||||||
## Architecture
|
## Architecture
|
||||||
|
|
||||||
@@ -127,12 +129,133 @@ with tool/JSON-mode support give better summarization quality;
|
|||||||
## Forcing a fresh digest now
|
## Forcing a fresh digest now
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
ssh ana-docker 'docker exec news-digest-worker python3 /app/digest.py'
|
ssh ana-docker 'docker exec news-digest-worker /usr/local/bin/run-digest.sh'
|
||||||
```
|
```
|
||||||
|
|
||||||
Runs the full pipeline once, ignoring cron. Useful after changing
|
Runs the full pipeline once, ignoring cron. Useful after changing
|
||||||
filtering knobs or adding feeds.
|
filtering knobs or adding feeds.
|
||||||
|
|
||||||
|
## Customizing the run schedule
|
||||||
|
|
||||||
|
Times come from two env vars on the worker, written into the busybox
|
||||||
|
crontab at container start. Standard 5-field cron syntax.
|
||||||
|
|
||||||
|
| Var | Default | Effect |
|
||||||
|
|---|---|---|
|
||||||
|
| `DIGEST_CRON_AM` | `0 8 * * *` | morning fire |
|
||||||
|
| `DIGEST_CRON_PM` | `0 20 * * *` | evening fire |
|
||||||
|
|
||||||
|
`busybox crond` honors `$NEWS_DIGEST_TZ` (defaults to
|
||||||
|
`America/Los_Angeles`), so values are interpreted in the configured TZ.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Shift the canonical instance to 7am / 6pm
|
||||||
|
ssh ana-docker '
|
||||||
|
cd /opt/docker/compose/news-digest
|
||||||
|
sed -i "s|^DIGEST_CRON_AM=.*|DIGEST_CRON_AM=0 7 * * *|" .env
|
||||||
|
sed -i "s|^DIGEST_CRON_PM=.*|DIGEST_CRON_PM=0 18 * * *|" .env
|
||||||
|
docker compose up -d --force-recreate news-digest-worker
|
||||||
|
'
|
||||||
|
```
|
||||||
|
|
||||||
|
Verify the rendered crontab:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ssh ana-docker 'docker exec news-digest-worker cat /etc/crontabs/root'
|
||||||
|
```
|
||||||
|
|
||||||
|
## Multi-tenant: one instance per teammate
|
||||||
|
|
||||||
|
Architecture: **shared miniflux + per-user digest stack**. Miniflux
|
||||||
|
already supports multi-user natively (each user has their own feeds,
|
||||||
|
categories, hide-state); we layer a separate news-digest stack per
|
||||||
|
user on its own port + output dir, scoped to that miniflux user's
|
||||||
|
credentials.
|
||||||
|
|
||||||
|
### Onboarding a new user
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Defaults — 8am / 8pm local, random password
|
||||||
|
scripts/add-digest-user.sh alice
|
||||||
|
|
||||||
|
# Custom hours
|
||||||
|
scripts/add-digest-user.sh bob --am "0 6 * * *" --pm "0 17 * * *"
|
||||||
|
|
||||||
|
# Weekday-only PM run
|
||||||
|
scripts/add-digest-user.sh carol --pm "30 18 * * 1-5"
|
||||||
|
|
||||||
|
# Pin a known password (still creates the miniflux user if missing)
|
||||||
|
scripts/add-digest-user.sh dan 'pickyourpassword' --am "0 9 * * *"
|
||||||
|
```
|
||||||
|
|
||||||
|
What the script does:
|
||||||
|
|
||||||
|
1. Reads miniflux admin creds from
|
||||||
|
`ana-docker:/opt/docker/compose/miniflux/.env`.
|
||||||
|
2. Allocates the next free `NEWS_DIGEST_PORT` (scans existing
|
||||||
|
`news-digest` + `digest-*` `.env` files).
|
||||||
|
3. Creates the miniflux user via the admin API. Already-exists is
|
||||||
|
non-fatal (kept; password not reset).
|
||||||
|
4. Provisions per-user dirs at
|
||||||
|
`/opt/docker/compose/digest-<user>/` and
|
||||||
|
`/opt/docker/data/digest-<user>/` (one-time sudo prompt — the
|
||||||
|
script falls back to printing the manual command if there's no TTY).
|
||||||
|
5. Materializes a per-user `.env` (inherits `NEWS_DIGEST_TAG` from the
|
||||||
|
canonical stack so all tenants run the same image).
|
||||||
|
6. Brings the stack up via `docker compose -p digest-<user> up -d`.
|
||||||
|
7. Runs `seed-headlines.py` against miniflux as the new user (creates
|
||||||
|
the World + Local categories with default feeds).
|
||||||
|
8. Triggers a first digest run so the page isn't blank.
|
||||||
|
|
||||||
|
Outputs the digest URL, miniflux login, and rendered cron schedule.
|
||||||
|
|
||||||
|
### Per-user file layout
|
||||||
|
|
||||||
|
```
|
||||||
|
ana-docker:
|
||||||
|
/opt/docker/compose/digest-<user>/ # compose + .env + build context
|
||||||
|
.env # auto-generated, contains MINIFLUX_PASSWORD
|
||||||
|
compose.yaml
|
||||||
|
Dockerfile + digest.py + ... # build context (image is shared/cached)
|
||||||
|
/opt/docker/data/digest-<user>/ # rendered HTML + per-user hidden.json
|
||||||
|
index.html
|
||||||
|
edition-YYYY-MM-DD-{am,pm}.html
|
||||||
|
hidden.json
|
||||||
|
.article-cache.json # extracted article text, 7-day TTL
|
||||||
|
```
|
||||||
|
|
||||||
|
Container names: `digest-<user>-worker` and `digest-<user>-web`.
|
||||||
|
|
||||||
|
### Updating an existing user's schedule
|
||||||
|
|
||||||
|
Re-running the script with the same username is idempotent:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Change alice's evening run to 5:30pm
|
||||||
|
scripts/add-digest-user.sh alice --pm "30 17 * * *"
|
||||||
|
```
|
||||||
|
|
||||||
|
The miniflux user is kept (password unchanged), the .env is
|
||||||
|
re-materialized with the new schedule, and the worker container is
|
||||||
|
recreated. Use the same flow to bump cron times, rotate passwords
|
||||||
|
(by passing a new one explicitly), or rerun feed seeding.
|
||||||
|
|
||||||
|
### Removing a user
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ssh ana-docker '
|
||||||
|
cd /opt/docker/compose/digest-alice
|
||||||
|
docker compose -p digest-alice down -v
|
||||||
|
'
|
||||||
|
# Optional: nuke compose dir + rendered output
|
||||||
|
ssh -t ana-docker 'sudo rm -rf /opt/docker/compose/digest-alice /opt/docker/data/digest-alice'
|
||||||
|
# Optional: delete the miniflux user via the admin UI at http://10.250.50.70:8080/
|
||||||
|
```
|
||||||
|
|
||||||
|
The digest is gone immediately; the miniflux account stays around
|
||||||
|
unless you delete it explicitly (cheap to leave; ~zero resource cost
|
||||||
|
when no stack is querying its feeds).
|
||||||
|
|
||||||
## Logs
|
## Logs
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
Reference in New Issue
Block a user