Skip to content

feat: super-admin panel with platform stats, users and API traffic - #168

Merged
TheCodeHeist-Coder merged 1 commit into
mainfrom
final-dashboard-appearing-issue
Oct 9, 2026
Merged

TheCodeHeist-Coder merged 1 commit into
mainfrom
final-dashboard-appearing-issue

Conversation

@TheCodeHeist-Coder

Copy link
Copy Markdown
Owner

What does this PR do?

Adds a super-admin panel for tracking the whole platform. It is two new apps, deployed by hand (Render + Vercel) and deliberately kept out of Docker and CI/CD:

  • apps/admin-server: Express admin API that reads the shared Postgres database.
  • apps/admin: React panel with these pages:
    • Overview: users, quizzes, session outcomes, participants, live sessions and daily charts.
    • Sessions: every hosted session, with leaderboard and per-question results; abandoned ones can be cancelled.
    • Quizzes: creator, organizers, sessions and question stats.
    • Users: name, email, sign-up, last login, activity, devices and IPs; CSV export.
    • API traffic: requests, unique IPs, error rates, latency, per-endpoint stats, browsers and platforms, failed requests.

Supporting changes in existing services:

  • http-server:
    • Logs every request to a new ApiRequestLog table. Writes are buffered and batched every 5s, so request latency is unaffected.
    • Deletes log rows older than 30 days (REQUEST_LOG_RETENTION_DAYS).
    • Writes any buffered rows on shutdown.
    • Records User.lastLoginAt on login.
  • packages/db: adds a new CANCELLED value to the SessionStatus enum. The panel can only cancel sessions idle for more than 3h; live ones are refused.
  • ws-server: quiz:start no longer revives a cancelled or already-finished session.
  • CI / Docker: the CI build step skips admin and admin-server, and .dockerignore excludes both, so existing images are unchanged.

Security:

  • Admin login is a single account defined by env vars. It is separate from the User table, so no app user can be promoted into it.
  • Admin tokens are signed with their own secret (which must differ from JWT_SECRET) and expire after 12h.
  • Five wrong passwords from one IP lock logins for 15 minutes.
  • The server refuses to start if any required env var is missing or too weak.
  • Password hashes are never sent to the panel.
  • CSV export escapes cells that a spreadsheet would run as formulas.

Related issue

No issue. This is a new feature, requested so the owner can track quiz activity, signups, users and API traffic in one place.

Type of change

  • Bug fix
  • New feature
  • Documentation
  • Refactoring
  • Performance
  • Tests
  • Build / CI / deployment

Which services does this touch?

  • apps/frontend
  • apps/http-server
  • apps/ws-server
  • apps/genAI
  • packages/db (schema or Prisma changes)
  • Docker / CI
  • New: apps/admin, apps/admin-server

How did you test this?

  • Migrations: applied all of them to a fresh Postgres. prisma migrate diff against schema.prisma reports no difference.
  • Admin API: seeded the database with 40 users, 20 quizzes, 50+ sessions, ~600 participants and 20k request logs, then called every endpoint across every date range.
    • All returned 200 with correct numbers.
    • The live count stayed correct with 25 concurrent sessions (the table lists 20).
    • Cancelling a live session is refused (409); cancelling an abandoned one works.
    • Login lockout blocks the 6th wrong attempt (429).
    • Requests from an unlisted origin get no CORS header.
  • http-server logger: routes are recorded as patterns (/api/v1/quizzes/:quizId), the client IP is taken from x-forwarded-for, and the user id is set on authenticated routes. On SIGTERM within 1s of a request, the buffered rows are written and the process exits in about 70ms.
  • Panel: screenshots of each page in headless Chrome at 1400px and 390px wide. No horizontal overflow, and the chart tooltips work.
  • Deploy commands: ran the documented Render and Vercel build commands from a clean copy with no node_modules.
    • The API started, passed /health and served data.
    • It refused to start when a secret was missing.
    • The panel build fails with a clear message when VITE_ADMIN_API_URL is unset.
    • No secret values appear in the panel's JS bundle.
  • Builds: http-server, ws-server, admin-server and admin all pass tsc / vite build.

Not tested: the ws-server quiz:start guard against a live Redis + ws-server (it is a one-line updateMany condition), and a full docker-compose up.

Checklist

  • I ran pnpm run lint (the new apps have no lint script)
  • I ran pnpm run check-types (type-checked each touched package individually instead)
  • I ran pnpm run build (per package: http-server, ws-server, admin-server, admin)
  • The app still starts with docker-compose up (not run; images are unchanged apart from the http-server and ws-server source edits)
  • I updated documentation if this changes behaviour or setup
  • I did not commit any .env file, API key or other secret (only .env.example templates)

Schema change: migration 20260929100000_admin_panel_tracking adds the ApiRequestLog table, User.lastLoginAt, and the CANCELLED enum value. It needs no manual step: the backend container applies pending migrations on startup. Deploy http-server before the admin API. Traffic data only starts accumulating from that deploy.

After merge: deploy apps/admin-server to Render and apps/admin to Vercel following apps/admin-server/README.md. The production database must be reachable from Render.

🤖 Generated with Claude Code

Adds two separately deployed apps, kept out of Docker and CI/CD:
- apps/admin-server: Express admin API (Render). Single env-defined admin
  login, JWT with its own secret, login lockout after 5 failures.
- apps/admin: React panel (Vercel). Overview, sessions, quizzes, users
  (with CSV export) and API traffic pages.

Supporting changes:
- http-server logs every request to ApiRequestLog in batches, prunes rows
  after 30 days, flushes on shutdown, and records User.lastLoginAt.
- New CANCELLED session status; the panel can cancel abandoned sessions,
  and ws-server no longer revives a cancelled or finished session.
- Migration 20260929100000_admin_panel_tracking.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@TheCodeHeist-Coder
TheCodeHeist-Coder merged commit 9e9a711 into main Oct 9, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant