Next.js 16 App Router, React 19, Prisma 7 on PostgreSQL 17, NextAuth v5, Tailwind v4. One application, run as a single Docker container per environment on AWS Lightsail, behind Caddy.
The request path
Inside the container a request goes: src/proxy.ts for the five protected prefixes, then the
route group's layout.tsx guard, then the page or a colocated actions.ts, then Prisma.
The two environments
| Production | Staging | |
|---|---|---|
| Host | www.deltechmun.in (apex redirects to www) and docs.deltechmun.in | test.deltechmun.in, noindex |
| Box | mun-prod, Lightsail Sydney | mun-staging, Lightsail Sydney |
| Branch | main | staging |
| Database | mun_prod on its own box | mun_staging on its own box |
| Media | deltechmun-media-prod (S3, ap-south-1) | deltechmun-media-staging |
They share nothing at runtime. Each GitHub Environment holds only its own box's address and host key, so a staging deploy cannot reach production.
Five things that will surprise you
Exactly one app container per environment, permanently. The quiz cache
(src/lib/quiz-cache.ts) and the realtime bus (src/lib/realtime/bus.ts) both live in
process memory. A second container would split them. Scaling out needs a shared cache
handler and an external bus first.
Middleware is called proxy.ts. Next 16 renamed it. Its matcher covers only /admin,
/write, /dashboard, /account and /recruitment.
The standalone server does not know its own URL. Its request.url is the bind address,
http://0.0.0.0:3000. The image pins AUTH_URL and sets AUTH_TRUST_HOST; never build an
absolute redirect from req.nextUrl. See authentication.
There is no test framework. Thirty-six standalone node:assert scripts plus a strings
gate, run by npm run check. See testing.
No component may contain a hardcoded string, and no source file an em dash. Both are enforced in CI. See the strings rule.
What the app depends on
| Dependency | Used for | If it is down |
|---|---|---|
| Postgres (container on the box) | All relational data | Hard dependency. /api/health returns 503. |
| S3 | Blog images, team photos, recruitment documents, hourly database backups | Uploads fail with a typed "not configured" error; existing pages still render. |
| Resend or SES | All outbound email, chosen by EMAIL_TRANSPORT | Failures are logged to EmailLog; the action still succeeds. |
| Razorpay | Card, netbanking and UPI payments | Falls back to UPI QR or a static link by configuration. |
| Groq | Import column suggestions | Optional. |
| GitHub Actions | Deploys, scheduled cron calls, health checks | The site keeps running; nothing new ships and crons do not fire. |
Realtime used to be Supabase. It is now /api/realtime, Server-Sent Events from the app
process itself. See the quiz system.
The infrastructure runbook
This site explains how the code works. Operating the boxes (creating one, restoring a backup, growing to 2 GB, rotating keys) is in docs/AWS.md in the repository.
Where to go next
- Local setupFrom a clean clone to a running dev server, including the database options.
- Environment variablesEvery variable the app reads, what it is for, and where to get its value.
- Project structureRoute groups, colocation rules, and where a new file belongs.
- The strings ruleNo hardcoded copy, no em dashes, and the CI gate that enforces both.
- Design systemThe token set, the custom dark variant, and the editorial utilities.
- UI componentsshadcn base-nova on Base UI, the variants, and the select gotcha.
- Themingnext-themes, the theme cookie, per-area toggles and portal theming.
- Data modelPrisma 7, the driver adapter, the model groups and the migration workflow.
- AuthenticationNextAuth v5, JWT sessions, the edge proxy, and session invalidation.
- AuthorizationRoute gates, server guards, safe redirects and the recruitment capability matrix.
- Server actionsThe shape every mutation follows: validate, guard, transact, audit, revalidate.
- Settings and contentThe Setting table, the content schema, and how event mode is derived.
- PaymentsThe provider abstraction, the Razorpay webhook, and the UPI fallback.
- EmailResend or SES, React Email templates, the delivery log and the staging sink.
- Media and uploadsTwo-phase presigned S3 uploads, the public prefixes, team photos, and the orphan sweep.
- The intake pipelineFour ways a delegate row is created, all through one function.
- The quiz systemServer-authoritative timing, realtime, scoring and sealed result receipts.
- Audit and rollbackWhat gets recorded, how the diff is stored, and how rollback replays it.
- Cron jobsThree scheduled routes, how they authenticate and how they stay idempotent.
- Security postureHeaders, the report-only CSP and how to promote it, rate limits, and the admin invariant.
- TestingThere is no test framework. There are thirty-six assertion scripts and a strings gate. Here is why.
- CI and deploymentThe workflows, the deploy pipeline to AWS, and what happens on a push to staging or main.
- API referenceEvery route handler: method, auth, payload and response.
- ContributingBranching, the checks you must run, and what a good pull request looks like.
- TroubleshootingThe errors you will actually hit locally, and what each one means.