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
|
||||
user opens the digest from).
|
||||
|
||||
Two editions per day: 8am and 8pm local. Plus per-edition archives
|
||||
at `/edition-YYYY-MM-DD-{am,pm}.html`.
|
||||
Two editions per day by default (0800 / 2000 local), parametrized via
|
||||
`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
|
||||
|
||||
@@ -127,12 +129,133 @@ with tool/JSON-mode support give better summarization quality;
|
||||
## Forcing a fresh digest now
|
||||
|
||||
```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
|
||||
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
|
||||
|
||||
```bash
|
||||
|
||||
Reference in New Issue
Block a user