Commit graph

6 commits

Author SHA1 Message Date
83ffa7dbf8 admin: front console via NPM + add purge-test-users CLI
Networking: the superadmin console now mirrors `app` instead of a
loopback-only host port. Base compose drops the host port; the dev
override binds 127.0.0.1:8091; the prod overlay joins the `intranet`
network and listens on :80 with --proxy-headers so NPM can proxy it.

CLI: add `purge-test-users` (dry-run by default, --commit to delete,
--keep allow-list defaulting to the real accounts). Deletes child rows
explicitly (DB-agnostic) plus email-keyed OTPs, so smoke-test signups
that were pointed at prod can be cleaned repeatably instead of via
ad-hoc SQL. Covered by 6 new tests.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-27 18:54:26 +02:00
411094d7b8 admin: internal-only superadmin console (users, payments, DB stats)
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>
2026-07-01 16:08:20 +02:00
9e058144ec deploy: uvicorn --proxy-headers so https stays https behind NPM
The app sits behind Nginx Proxy Manager which terminates TLS and
forwards plain HTTP. Without --proxy-headers, Starlette's request.url
keeps the inbound \`http\` scheme, so url_for() renders the static-CSS
\`<link>\` as http://… and the browser blocks it as mixed content under
the public https origin.

Adds --proxy-headers + --forwarded-allow-ips=* to the prod uvicorn
command. The wildcard is fine because the container has no host port —
only the intranet bridge (where NPM lives) can reach it.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 21:47:48 +01:00
3932ef4048 deploy: prod uses container-FQDN for DATABASE_URL / REDIS_URL
The shared \`intranet\` Docker network has many other containers
aliased as \`db\` and \`redis\` (mastodon-db, mailman redis, etc.).
Docker's embedded DNS resolved those before our own services, sending
the app's MySQL connection to the wrong IP and crashing it in a restart
loop.

Switched both DATABASE_URL and REDIS_URL in the prod overlay to use
the project-prefixed container names (readmarkets-db-1,
readmarkets-redis-1), which are globally unique on the Docker daemon.
Applied to both app and scheduler.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 21:34:57 +01:00
a50c5091c4 deploy: split compose into base (prod-ready) + dev override
Compose merges list-typed fields like \`ports\` by concatenation, so the
previous prod overlay couldn't clear the base file's host port binding;
the VPS app ended up listening on both port 80 (intranet) AND host port
8800 simultaneously.

Restructured to the conventional dev/prod split:
- docker-compose.yml: no host port — prod-ready by default
- docker-compose.override.yml: dev-only host port binding (auto-loaded
  by \`docker compose up\` locally, skipped when prod uses explicit -f)
- docker-compose.prod.yml: command-port 80 + intranet network only

Production invocation:
  docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 21:30:28 +01:00
7364d11ffe deploy: add prod compose overlay (no host port, joins intranet network)
The VPS deployment sits behind Nginx Proxy Manager on a pre-existing
`intranet` Docker bridge network. The overlay drops the host port
binding from the base compose, switches uvicorn to listen on port 80
inside the container (uniform NPM upstreams), and joins the app to
both `default` (for db/redis) and `intranet` (for NPM ingress).

Apply with:
  docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d

Local dev (compose without the overlay) is unchanged — still binds
the host port from CASSANDRA_PORT in .env.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 21:26:55 +01:00