74 lines
2.4 KiB
Markdown
74 lines
2.4 KiB
Markdown
# surf.nathan.rip
|
|
|
|
Momentum Mod surf replay hosting - video + `.mtv` replay files with automatic map tier badges, stage splits, and PB history.
|
|
|
|
## Architecture
|
|
|
|
| Layer | Stack |
|
|
|-------|-------|
|
|
| Frontend | React + Vite + Tailwind CSS v4 |
|
|
| Backend | ElysiaJS (Bun) |
|
|
| Storage | RustFS (S3-compatible) |
|
|
| Database | SQLite |
|
|
|
|
## How it works
|
|
|
|
1. Upload a video + `.mtv` replay file via the auth-gated upload page
|
|
2. Backend parses the `.mtv` binary header for metadata (map, player, time, Steam ID, stage splits)
|
|
3. Map tier and thumbnail are fetched from the Momentum Mod API
|
|
4. Video uploads go directly to RustFS via presigned URLs (no size limit)
|
|
5. If a better time is uploaded for the same map+player, the PB is updated and the old run is archived as a previous PB
|
|
|
|
## Setup
|
|
|
|
```bash
|
|
# Backend
|
|
cd backend
|
|
cp .env.example .env # edit with your credentials
|
|
bun install
|
|
bun run dev
|
|
|
|
# Frontend
|
|
cd frontend
|
|
bun install
|
|
bun run dev
|
|
```
|
|
|
|
The frontend dev server proxies `/api` to `localhost:3001`.
|
|
|
|
## Environment variables
|
|
|
|
| Variable | Required | Description |
|
|
|----------|----------|-------------|
|
|
| `ADMIN_TOKEN` | Yes | Bearer token for upload/delete auth |
|
|
| `RUSTFS_ENDPOINT` | No | S3 endpoint (default: `http://localhost:9000`) |
|
|
| `RUSTFS_ACCESS_KEY` | No | S3 access key |
|
|
| `RUSTFS_SECRET_KEY` | No | S3 secret key |
|
|
| `RUSTFS_BUCKET` | No | S3 bucket name (default: `surf`) |
|
|
| `RUSTFS_PUBLIC` | No | Public read access (default: `true`) |
|
|
| `PORT` | No | Server port (default: `3001`) |
|
|
| `DB_PATH` | No | SQLite path (default: `./data/surf.db`) |
|
|
| `STATIC_DIR` | No | Frontend static files (default: `./public`) |
|
|
|
|
## RustFS setup
|
|
|
|
1. Create a bucket called `surf` in your RustFS instance
|
|
2. Set bucket to public read for direct video URLs
|
|
3. Configure CORS on the bucket to allow `PUT` from your frontend origin (required for presigned video uploads)
|
|
4. Create an IAM policy with `s3:ListBucket`, `s3:GetObject`, `s3:PutObject`, `s3:DeleteObject` on `surf/*`
|
|
|
|
## .mtv file format
|
|
|
|
195-byte binary header followed by JSON stats and LZMA-compressed replay data.
|
|
|
|
| Offset | Size | Field |
|
|
|--------|------|-------|
|
|
| 0x00 | 4 | Magic (`MMTV` = 0x56544D4D) |
|
|
| 0x04 | 4 | Version |
|
|
| 0x10 | 64 | Map name (null-terminated) |
|
|
| 0x50 | 41 | Map hash |
|
|
| 0x7B | 4 | Tick interval (float) |
|
|
| 0x7F | 8 | Steam ID (uint64) |
|
|
| 0x87 | 32 | Player name (null-terminated) |
|
|
| 0xA9 | 8 | Run time (double) |
|
|
| 0xB1 | 4 | Total ticks | |