noise -> signal

000

press any key to skip

Role-based cricket video platform: analysts manage matches and upload clips to S3

Problem

Cricket analysts need a structured way to organize tournament/match video footage per player and share it with players, instead of ad-hoc file sharing (README is boilerplate create-next-app text; problem inferred from code).

What it does

A Next.js app with three roles (admin, video_analyst, player). Admins manage users (create/edit/delete, app/api/admin/*), analysts create tournaments and matches, assign players, and batch-upload videos via S3 presigned URLs; players get a dashboard of their tournaments/matches/videos. JWT httpOnly-cookie auth with role-based dashboard routing in proxy.ts.

Architecture

[web] Next.js 16 App Router UI (login + 3 role dashboards) (client)
[api] Next.js API routes (auth, admin, analyst, videos) (api)
[db] PostgreSQL (Neon) via pg Pool (store)
[s3] AWS S3 video bucket (store)
[auth] JWT cookie auth + role middleware (proxy.ts) (worker)

web ──▶ api · fetch JSON (login, CRUD, presign, save)
api ──▶ auth · verify JWT from 'token' cookie
api ──▶ db · SQL via pg pool (users, tournaments, matches, match_players, players, videos)
api ──▶ s3 · generate presigned PUT URLs (15 min), create folder placeholders
web ──▶ s3 · direct PUT upload with XHR retry/backoff (lib/upload-videos.ts)
web ──▶ api · /api/videos/save records successful uploads in DB

Simplified — box-and-arrow sketch of the repo structure, not a system diagram.

Decisions

  1. 01

    Direct-to-S3 uploads via presigned URLs

    Large video files bypass the server; presign route validates analyst ownership, match membership, and builds sanitized folder keys (player/tournament/match) before signing

    Evidence: app/api/videos/presigned-urls/route.ts; lib/upload-videos.ts

  2. 02

    Client-side upload orchestration with retry and partial-failure handling

    uploadWithRetry does 3 attempts with exponential backoff and Promise.allSettled so individual video failures don't abort the batch; only succeeded files are saved to DB

    Evidence: lib/upload-videos.ts

  3. 03

    JWT cookie with role-based dashboard routing

    Single proxy() rewrites /dashboard/* to the role-specific dashboard and redirects unauthorized roles to /restricted

    Evidence: proxy.ts

  4. 04

    Zod schemas discriminated by gender with separate category enums

    Men categories (under_14..ranji) differ from women (under_16..senior); reused across player and tournament creation

    Evidence: types/zod.ts

  5. 05

    Separate presign then save steps

    DB rows for videos are only written after the client confirms the S3 PUT succeeded, avoiding orphaned records for failed uploads

    Evidence: app/api/videos/save/route.ts; lib/upload-videos.ts

The hard parts

  • Authorization matrix in the presign route: analyst must own the tournament, players must be match members, tournament/match mappings must be consistent, and adhoc vs match uploads can't be mixed (app/api/videos/presigned-urls/route.ts:126-296)
  • S3 key layout with sanitized player/tournament/match folder segments plus zero-byte 'folder placeholder' objects created on demand (app/api/videos/presigned-urls/route.ts:334-388)
  • Batch upload resilience: per-file retry with backoff, progress reporting, and partial-batch save semantics (lib/upload-videos.ts:24-160)

Known limitations

  • Player backend is unfinished: app/api/player/[userId]/dashboard/route.ts and app/api/player/videos/[videoId]/play/route.ts are empty 0-byte files, so the player dashboard and video playback API have no implementation
  • Several analyst routes (create_match, create_tournament, delete_*) perform no JWT/role check on the request, unlike the videos routes (inferred from app/api/video_analyst/create_match/route.ts)
  • JWT falls back to the hardcoded string 'fallback_secret' when JWT_SECRET is unset (proxy.ts:14, app/api/login/route.ts:66)
  • No video analysis itself — despite the name, the app manages and stores footage but contains no analytics/ML code
  • No tests, no CI, no Docker, no env example file

Command palette

Jump to a section, project or link