Files
Final-Year-Project/Backend
2026-02-20 14:00:00 +00:00
..
2025-12-06 10:15:00 +00:00
2025-12-06 10:15:00 +00:00
2025-12-06 10:15:00 +00:00

backend

Overview

Backend for the video upload prototype providing:

  • Better Auth email/password authentication
  • Presigned MinIO uploads/downloads
  • An authenticated video administration surface at /admin

Requirements

  • Bun (tooling used for running scripts & dependency management)
  • Postgres reachable via DATABASE_URL
  • MinIO-compatible storage reachable via MINIO_* env vars
  • .env file populated with secrets and credentials

Install

bun install

Configuration

Copy the example environment file and adjust the values:

cp .env.example .env

Required env vars:

Name Purpose
DATABASE_URL Postgres connection string
BETTER_AUTH_SECRET Secret used to sign sessions
BETTER_AUTH_BASE_URL Public base URL for the backend (e.g., http://localhost:3000)
BETTER_AUTH_TRUSTED_ORIGINS Comma-separated list of allowed frontend origins
PORT HTTP port (default 3000)
MEDIA_MODE Media runtime mode (legacy default, single_server_sfu scaffold mode)
MEDIA_PROVIDER Media backend provider (mock by default)
TURN_URLS / TURN_USERNAME / TURN_CREDENTIAL TURN/STUN configuration used by single-server SFU mode
MEDIA_RECORDINGS_DIR Local output directory for server-side recording workers (planned in SFU mode)
MEDIA_MAX_PUBLISHERS / MEDIA_MAX_SUBSCRIBERS_PER_ROOM Soft concurrency limits for single-server media mode (planned)
MINIO_* Connection settings for the MinIO/S3 endpoint
ADMIN_USERNAME / ADMIN_PASSWORD Basic auth for /admin dashboard

BETTER_AUTH_URL is still accepted as a legacy fallback, but BETTER_AUTH_BASE_URL is preferred.

Running

  • Start the server in development:
bun run dev
  • Server boots after ensuring the configured MinIO bucket exists.

Database (Drizzle ORM)

  • Generate a migration:
    bun run db:generate
    
  • Apply migrations:
    bun run db:migrate
    
  • Backfill Better Auth credential accounts for existing users:
    bun run auth:migrate
    
  • Open Drizzle Studio:
    bun run db:studio
    

API

All /videos and /admin routes require a valid Better Auth session except for the admin dashboard access, which uses HTTP Basic auth with ADMIN_USERNAME/ADMIN_PASSWORD.

Authentication

Authentication is handled by Better Auth under /api/auth/* (for example /api/auth/sign-in and /api/auth/sign-up).

Authorization

All authenticated endpoints expect a Better Auth session cookie sent by the client.

Video Management

Endpoint Purpose
POST /videos/upload-url Request a presigned PUT URL for a new video
GET /videos/download-url Generate a signed GET URL to download a video
GET /videos List objects in the configured bucket
DELETE /videos Delete an object by objectKey

POST /videos/upload-url request body requires fileName and deviceId (UUID belonging to the authenticated user), with optional prefix.

Device Management (Phase 1)

Endpoint Purpose
POST /devices/register Register a user-owned device as camera or client and issue bearer device token
GET /devices List all devices for the authenticated user
PATCH /devices/:deviceId Update device role/metadata/status
POST /devices/:deviceId/heartbeat Device-token authenticated presence heartbeat

Camera-Client Linking (Phase 1)

Endpoint Purpose
POST /device-links Link one client device to one camera device
GET /device-links List links for the authenticated user
DELETE /device-links/:linkId Remove a camera-client link

Realtime Commands (Phase 2)

Endpoint Purpose
POST /commands Queue and dispatch command from a linked client device to camera
GET /commands Inspect command status/history
POST /commands/:commandId/ack Device-token ack/reject command fallback

Socket.IO channel:

  • Devices connect with bearer device token (auth.token or Authorization: Bearer ...).
  • Camera receives command:received.
  • Camera sends command:ack with acknowledged or rejected.
  • Source client receives command:status.

Motion Events (Phase 3)

Endpoint Purpose
POST /events/motion/start Camera device creates motion event and fan-outs notification to linked clients
POST /events/:eventId/motion/end Camera device closes a motion event and broadcasts end state
GET /events Authenticated user fetches event history

Motion realtime events:

  • Linked clients receive motion:detected as soon as camera starts event.
  • Linked clients receive motion:ended when camera ends event.

On-Demand Streams + Media Credentials (Phase 4 + 5)

Endpoint Purpose
POST /streams/request Client device requests a linked camera to start a live stream
POST /streams/:streamSessionId/accept Camera device accepts and transitions stream session to streaming
GET /streams/:streamSessionId/publish-credentials Camera fetches media ingest credentials for the active stream session
GET /streams/:streamSessionId/subscribe-credentials Viewer fetches media subscribe credentials for the active stream session
POST /streams/:streamSessionId/end Requester/camera ends an existing stream session
GET /streams/:streamSessionId/playback-token Obtain short-lived playback token for active stream
GET /streams/me/list List stream sessions for the current device

Stream realtime events:

  • Client receives stream:requested after request creation.
  • Client receives stream:started when camera accepts.
  • Both devices receive stream:ended when session is closed.

Experimental SFU scaffolding endpoints (MEDIA_MODE=single_server_sfu):

  • GET /streams/:streamSessionId/sfu/session fetch in-memory SFU session state for participant devices
  • POST /streams/:streamSessionId/sfu/publish-transport camera creates publish transport descriptor
  • POST /streams/:streamSessionId/sfu/subscribe-transport participant creates subscribe transport descriptor

Streaming Scale Tradeoffs (Current Prototype)

  • The current implementation is not production-grade at scale.
  • Video quality and reliability currently depend on direct browser-to-browser WebRTC success, with a low-fps frame relay fallback in the simulator.
  • This backend currently acts as a control plane (commands, session state, credentials, events), not a full media plane/SFU.
  • Running live transport + fan-out + recording on the same web server is possible for small loads but introduces significant CPU, RAM, and network egress pressure under concurrency.
  • For larger deployments, use a dedicated media plane (managed or self-hosted SFU + recorder) and keep this service focused on auth/session/control APIs.
  • For a pragmatic prototype path that keeps media on the current server, see docs/streaming-on-web-server-plan.md.
  • MEDIA_MODE=single_server_sfu currently enables scaffolding only (interfaces/config/health visibility), not full SFU media routing yet.

API Docs

OpenAPI docs are generated from Zod/OpenAPI definitions:

Endpoint Purpose
GET /openapi.json OpenAPI 3 spec (JSON)
GET /docs Swagger UI

Web Mobile Simulator

Use GET /sim/mobile-sim.html to run a browser simulator that behaves like the mobile app:

  • Register as camera or client
  • Connect Socket.IO with bearer device token
  • Camera: process incoming start_stream commands, fetch publish credentials, start/end motion events
  • Client: create links, request streams, fetch subscribe credentials, and fetch playback tokens

Admin Dashboard

Access /admin with Basic auth to:

  • Request presigned upload URLs
  • Upload files directly via the generated URL
  • List and delete objects within the MinIO bucket

The dashboard UI submits to /admin/upload-url, /admin/objects, and /admin/object.

Schema

  • users email/username/password and timestamps
  • events user-created events with a unique videoUrl
  • videos upload metadata including objectKey, bucket, URLs, status, and timestamps

Notes

  • MinIO bucket creation happens during startup, so the service must be able to reach the endpoint.
  • Keep Better Auth and MinIO secrets out of source control.