memoryENGINE_CORE::PORTFOLIO
ROOTDOCSSRCLOG
terminal
© 2026 ENGINE_CORE // STABLE_BUILD
VULKAN_SDKRUST_STABLEC++20NEXT.JS
homeROOTdescriptionDOCSfolder_openSRCarticleLOG
arrow_back[RETURN_TO_SRC]

SRC://BRCA-WEBSITE

github.com/ArnavChoudhary9/brca-website · status: COMPILING · updated 13d ago

TypeScript 0
VIEW_SOURCELIVE_DEMO

BRCA Website

Cultural events portal for the Board for Recreational and Cultural Activities, IIT Delhi — clubs, competitions, calendar, public submissions, inter-hostel trophy.

Built on Next.js 16 (App Router), Postgres, MinIO, nginx. Deployed as a single Docker compose stack.


Quick start (dev)

# 1. Bring up Postgres (5434) and MinIO (api 9100, console 9101)
npm run db:up

# 2. Copy env template and adjust if needed
cp .env.example .env

# 3. Install deps, migrate the schema, seed sample data
npm install
npm run db:migrate
npm run db:seed     # prints the seeded user list + default password

# 4. Run Next.js
npm run dev

Open http://localhost:3000. Sign in at /login with any seeded user (default password changeme).

UsernameRole
tanvi.saxena.gsecGSec
vikram.hegde.dgsecD-GSec
aarav.mehta.danceDance Secy
karan.bhatia.musicMusic Secy

(Full list printed by npm run db:seed.)

Useful dev scripts

ScriptWhat it does
npm run db:upStart Postgres + MinIO via compose.dev.yml
npm run db:downStop them (keeps data)
npm run db:resetStop, wipe volumes, restart
npm run db:logsFollow Postgres + MinIO logs
npm run db:generateGenerate a new Drizzle migration from the schema
npm run db:migrateApply pending migrations
npm run db:seedSeed the DB from src/data/*.json
npm run db:studioOpen Drizzle Studio (DB browser)
npm run devStart Next.js
npm run buildProduction build (standalone output)
npm run lintESLint

MinIO console: http://localhost:9101 — login with MINIO_ROOT_USER / MINIO_ROOT_PASSWORD from .env.


Data layer

All persistent state lives in Postgres. Drizzle schema: src/db/schema.ts. Files (banners, rulebooks, avatars, contest submissions) live in MinIO under bucket brca.

The site loads from the JSON files in src/data/ only at seed time (src/db/seed.ts). Once seeded, edits happen exclusively through /admin.

Adding/changing content

Sign in as a Secy and:

  • Competitions — /admin/competitions/new. Fill in title, dates, description, weight, optional submission window. Save as draft, then upload banner / rulebook PDF, then submit for review. GSec/D-GSec approves at /admin/approvals.
  • Events — /admin/events/new. Same workflow.
  • Announcements — /admin/announcements/new. Per-club or BRCA-wide (officers only).
  • Winners + points — open a competition → "Winners & points". Enter position + points per hostel. Trophy page reflects within ~1s.
  • Public submissions — when a competition has submissionsOpenAt/submissionsCloseAt set and is approved, anyone can upload a file at /comp/<slug>/submit. Secys see entries at /admin/competitions/<id>/submissions.

Approval workflow

  • Secy submits → status pending → visible to GSec/D-GSec at /admin/approvals.
  • GSec/D-GSec submits → auto-publishes (status approved).
  • An officer cannot approve their own submission (audit-logged in audit_log table).
  • Self-edits to already-published items keep them published.

Trophy

/trophy shows:

  • An Overall tab — sum across all clubs of winner.points × competition.weight per hostel.
  • One tab per club — same formula, scoped to that club's competitions, plus a per-comp breakdown showing each competition's positions, raw points, and weighted points.

The single weight field on a competition applies in both club and overall rankings.


Directory structure

src/
  app/                      # Next.js App Router routes
    (public)                # / · /clubs · /clubs/[id] · /comp/[id]
    comp/[id]/submit/       # public submission form
    admin/                  # role-gated admin pages
    login/ · logout/        # auth pages
  components/
    layout/                 # Navbar · Footer
    cards/                  # ClubCard · EventCard · CompetitionCard · TeamMemberCard
    calendar/               # multi-day calendar with mobile-friendly grid
    trophy/                 # tabbed leaderboard
    admin/                  # AdminShell · forms · upload widgets
    ui/                     # shadcn primitives (auto-generated)
  data/                     # JSON seed source (clubs/events/comps/team/trophy)
  db/                       # schema · migrations · seed · drizzle client
  lib/
    actions/                # Server Actions (CRUD + uploads + approvals)
    auth/ · auth.ts         # bcrypt + iron-session + DB sessions
    queries/trophy.ts       # tabbed-trophy SQL aggregation
    schemas/                # zod schemas
    storage.ts              # @aws-sdk/client-s3 wrapper for MinIO
    data.ts · types.ts      # public data layer (consumed by all public pages)
    date.ts                 # native Intl-only date helpers
  proxy.ts                  # Next.js 16 proxy (replaces middleware) gating /admin/*
nginx/default.conf          # prod reverse proxy: /assets/ → MinIO, / → Next
Dockerfile                  # multi-stage: deps → build → runner (standalone) + migrator
compose.dev.yml             # dev: Postgres + MinIO only; Next runs on host
compose.prod.yml            # prod: postgres + minio + migrate + next + nginx

Production

Single host, single network, single port (80).

# 1. Set production env
cp .env.example .env
# Edit .env: strong POSTGRES_PASSWORD, SESSION_SECRET, MINIO_ROOT_PASSWORD

# 2. Build + start the full stack
docker compose -f compose.prod.yml up -d --build

# 3. (Once, optional) Seed sample data
docker compose -f compose.prod.yml --profile setup run --rm seed

The site is now at http://<host>/. nginx fronts:

  • /_next/static/... → Next.js (long-cache)
  • /assets/<key> → MinIO bucket brca (long-cache, immutable)
  • everything else → Next.js on :3000

After first up: log in as GSec (default tanvi.saxena.gsec / changeme if you seeded), immediately change the password at /admin/profile, then create real officer accounts at /admin/users and delete the seeded ones.

compose.prod.yml does not publish Postgres or MinIO ports to the host — they're only reachable inside the compose network. Submission files are publicly readable via nginx because the bucket has a download policy; if you need private submissions, gate the path in nginx and add an authenticated download route.

Migrations on deploy

The migrate service runs once at up and exits. next won't start until migrations succeed.

TLS

Not handled here. Front this stack with a reverse proxy that terminates TLS (Caddy, Cloudflare, an nginx ingress) and forwards :80.


Tech notes

  • Next.js 16 — proxy.ts (not middleware.ts); cookies(), headers(), route params are all async; output: "standalone" for Docker.
  • Drizzle ORM + postgres-js — type-safe queries, no ORM client codegen. Migrations under src/db/migrations/.
  • iron-session — encrypted cookie wrapping a session id; sessions table in DB so they're revokable.
  • shadcn/ui (base-nova style) on Tailwind v4 — Button, Card, Tabs, Input, Label, Textarea, Dialog, DropdownMenu, Sonner, Avatar, etc.
  • MinIO is S3-compatible — switching to AWS S3 is a config change in .env (no code change).

License

Internal IIT Delhi project. No license declared.