184 lines
7.3 KiB
Markdown
184 lines
7.3 KiB
Markdown
# 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](https://bun.sh) (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
|
||
```bash
|
||
bun install
|
||
```
|
||
|
||
## Configuration
|
||
Copy the example environment file and adjust the values:
|
||
|
||
```bash
|
||
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_PROVIDER` | Media backend provider (`mock` by default) |
|
||
| `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:
|
||
|
||
```bash
|
||
bun run dev
|
||
```
|
||
|
||
- Server boots after ensuring the configured MinIO bucket exists.
|
||
|
||
## Database (Drizzle ORM)
|
||
- Generate a migration:
|
||
```bash
|
||
bun run db:generate
|
||
```
|
||
- Apply migrations:
|
||
```bash
|
||
bun run db:migrate
|
||
```
|
||
- Backfill Better Auth credential accounts for existing users:
|
||
```bash
|
||
bun run auth:migrate
|
||
```
|
||
- Open Drizzle Studio:
|
||
```bash
|
||
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.
|
||
|
||
#### 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`.
|
||
|
||
### 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.
|