# 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 - **Dev:** bound to **`127.0.0.1:8091`** on the host (loopback only, from `docker-compose.override.yml`). Open . - **Prod:** no host port. The container joins the `intranet` network and listens on port 80, so **Nginx Proxy Manager** fronts it like the main app (upstream `readmarkets-admin-1:80`). Point an NPM proxy host at it and, ideally, add an NPM access list / basic-auth as a second layer. - 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`). > The console is now reachable on whatever public hostname NPM maps to it — > it is no longer air-gapped behind an SSH tunnel. Keep `ADMIN_CONSOLE_PASSWORD` > strong and prefer adding an NPM access rule in front of it. ## 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 # 3. in Nginx Proxy Manager: add a proxy host → forward to readmarkets-admin-1 # port 80 (both containers are on the `intranet` network). ``` ## Tests ```sh docker compose -f docker-compose.test.yml run --rm test pytest tests/test_admin_console.py -v ```