New independent `admin` service (admin.main:app) on the same image, reusing app.db/app.models read-only. Never runs migrations or the scheduler; issues SELECTs only. - Password-gated (ADMIN_CONSOLE_PASSWORD) with a 12h signed cookie; closed by default when the password is empty. - Bound to 127.0.0.1:8091 (SSH-tunnel access); off the intranet/NPM network. - Pages: overview stats, user list + search, per-user history/payment detail, DB usage (information_schema size + row estimates). - Compose: base `admin` service (+prod DB-host override, test mount); Dockerfile bakes admin/ into runtime + test stages. - Tests: tests/test_admin_console.py (auth, queries, page wiring) — 12 passing. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
50 lines
1.8 KiB
Markdown
50 lines
1.8 KiB
Markdown
# Superadmin console
|
|
|
|
An **independent, internal-only** web service for the operator: user list with
|
|
per-user history + payment status, and database usage stats. Runs in its own
|
|
container (`admin` service) off the same image as the main app, reusing
|
|
`app.db` + `app.models`, but it is a separate FastAPI app (`admin.main:app`)
|
|
that **never** runs migrations or the scheduler and only ever issues `SELECT`s.
|
|
|
|
## Access model
|
|
|
|
- Bound to **`127.0.0.1:8091`** on the host — never exposed publicly, not on
|
|
the `intranet`/NPM network. Reach it over an SSH tunnel:
|
|
|
|
```sh
|
|
ssh -L 8091:localhost:8091 <vps>
|
|
# then open http://localhost:8091
|
|
```
|
|
|
|
- Gated by a single shared password, `ADMIN_CONSOLE_PASSWORD` (in `.env`).
|
|
Empty password ⇒ every login is refused (closed by default). The login sets
|
|
a 12-hour signed cookie (`admin_console_session`).
|
|
|
|
## Pages
|
|
|
|
- `/` — overview: totals, tier split, paid-active, signups 7/30d, sync count,
|
|
referral conversions, newest users.
|
|
- `/users` — full user table with email search + paging.
|
|
- `/users/{id}` — one user: account, computed paid status, Stripe/Polar
|
|
linkage, cloud-sync state, referrals sent, feedback votes, digest-email log,
|
|
legal acknowledgements.
|
|
- `/db` — per-table row estimates and on-disk size (from
|
|
`information_schema.tables`), with each table's share of total.
|
|
|
|
## Deploy (this host runs prod)
|
|
|
|
Adding a **new** service means `up -d` (a plain `restart` won't create it).
|
|
Do NOT run a bare `docker compose up` on this host — always pass the prod
|
|
overlay:
|
|
|
|
```sh
|
|
# 1. set ADMIN_CONSOLE_PASSWORD in .env
|
|
# 2. build + create just the admin container (leaves app/scheduler/db running)
|
|
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d --build admin
|
|
```
|
|
|
|
## Tests
|
|
|
|
```sh
|
|
docker compose -f docker-compose.test.yml run --rm test pytest tests/test_admin_console.py -v
|
|
```
|