Refactor load testing scripts to support Bash and PowerShell
CI / test (push) Successful in 2m50s
CI / docker (push) Successful in 5m12s

- Updated README.md to reflect changes in load testing commands and added usage examples for Bash scripts.
- Converted PowerShell load testing scripts to Bash equivalents for better cross-platform compatibility.
- Added new Bash scripts: run-loadtests.sh, summarize-artillery.sh, and summarize-k6.sh to mirror existing PowerShell functionality.
- Updated admin-api.js and ws-chat.yml for consistency and improved performance metrics reporting.
- Enhanced error handling and output formatting in summary scripts for better readability.
This commit is contained in:
zwitschi committed 2026-09-02 19:47:34 +02:00
1 parent 9f60087a4c
commit 33b6296c5e
82 files changed
+19682 -21133

No files matched your search

+68 -216
View File
@@ -1,216 +1,101 @@
# API Reference
# Linear Void API Reference
This document describes the currently implemented API surfaces in Linear Void.
Base URLs: Public/admin app `https://linearvoid.tv` · WebSocket `wss://linearvoid.tv/ws` · Local fallback `http://localhost` / `ws://localhost/ws`
## Base URLs
## Authentication
- Public app and admin: <https://linearvoid.tv>
- WebSocket endpoint: <wss://linearvoid.tv/ws>
- Local fallback: <http://localhost> and <ws://localhost/ws>
Admin REST endpoints use a cookie session:
## Authentication Model
Admin REST endpoints use a cookie session.
- Cookie name: linearvoid_admin_session
- Cookie attributes: HttpOnly, SameSite=Strict, Secure when request is HTTPS (or X-Forwarded-Proto=https)
- Session TTL: controlled by ADMIN_SESSION_TTL_HOURS (default 12h)
- Cookie: `linearvoid_admin_session` — `HttpOnly`, `SameSite=Strict`, `Secure` when request is HTTPS (or `X-Forwarded-Proto=https`)
- Session TTL: `ADMIN_SESSION_TTL_HOURS` (default 12h)
## Chat WebSocket API
### Connect
Connect: `GET /ws?username={name}&room={room}` — client/server exchange JSON messages.
Endpoint:
| Message (client→server) | Field | Notes |
| ----------------------- | -------------------------------------------------------------------------------- | ----- |
| `type` | `public` for room chat, `private` for DM routing; `system` for moderation/events | |
| `room` | `main`; private DMs use `dm:userA-userB` (usernames sorted) | |
| `username` | sender | |
| `text` | message body | |
- GET /ws?username={name}&room={room}
Client sends JSON messages over the socket.
### Message Envelope (client -> server)
Server→client envelope adds `"timestamp"` (ISO-8601, e.g. `2026-08-04T12:34:56Z`).
```json
{
"room": "main",
"username": "viewer123",
"text": "hello world",
"type": "public"
}
```
Notes:
- type is usually public for room chat and private for DM room routing.
- Private room names are formatted like dm:userA-userB (sorted usernames).
- Moderation/system events are sent as type=system messages.
### Message Envelope (server -> client)
```json
{
"room": "main",
"username": "viewer123",
"text": "hello world",
"type": "public",
"timestamp": "2026-08-04T12:34:56Z"
}
{ "room": "main", "username": "viewer123", "text": "hello world", "type": "public" }
```
## Admin Session API
### Login
| Method & Path | Auth | Description |
| ------------------------ | ----------------------------------- | ----------------------------- |
| `POST /api/admin/login` | none | Authenticate |
| `POST /api/admin/logout` | optional (clears cookie either way) | Logout |
| `GET /api/admin/session` | cookie required | Session check; 401 if invalid |
- POST /api/admin/login
- Auth: none
Request:
Login request:
```json
{
"username": "admin",
"password": "your-password"
}
{ "username": "admin", "password": "your-password" }
```
Success response:
Login/logout/session success response (session returns 401 on failure):
```json
{
"authenticated": true,
"username": "admin"
}
{ "authenticated": true, "username": "admin" }
```
### Logout
Logout response: `{ "authenticated": false }`
- POST /api/admin/logout
- Auth: optional (clears cookie either way)
## Admin Data API (auth required)
Response:
| Method & Path | Returns |
| ----------------------------------------- | ---------------------------------------------------------------------------------------------------- |
| `GET /api/admin/overview` | Aggregate counters + runtime/system snapshot |
| `GET /api/admin/playlist` | Playlist |
| `PUT /api/admin/playlist` | Set playlist (body: `{ "content": "file '/app/media/video1.mp4'\nfile '/app/media/video2.mp4'\n" }`) |
| `GET /api/admin/schedule` | Parsed playlist entries + matched media metadata/runtime |
| `GET /api/admin/moderation/logs?limit=50` | Moderation logs |
| `POST /api/admin/moderation/actions` | Perform moderation action |
| `GET /api/admin/analytics` | Totals (messages, moderation actions, rooms) + top room summary |
| `GET /api/admin/system` | Health snapshot (chat/playout/redis/database) + config metadata |
| `GET /api/admin/users` | Presence users (room + username) when Redis available |
Moderation action request:
```json
{
"authenticated": false
}
{ "action": "mute", "target": "username-or-id", "moderator": "admin", "reason": "spam", "room": "main" }
```
### Session Check
- GET /api/admin/session
- Auth: cookie required
Success response:
```json
{
"authenticated": true,
"username": "admin"
}
```
Failure:
- 401 unauthorized
## Admin Data API
All endpoints below require admin authentication.
### Overview
- GET /api/admin/overview
Returns aggregate counters and runtime/system snapshot fields.
### Playlist
- GET /api/admin/playlist
- PUT /api/admin/playlist
PUT request:
```json
{
"content": "file '/app/media/video1.mp4'\nfile '/app/media/video2.mp4'\n"
}
```
### Schedule
- GET /api/admin/schedule
Returns parsed playlist entries plus matched media metadata/runtime.
### Moderation
- GET /api/admin/moderation/logs?limit=50
- POST /api/admin/moderation/actions
Action request:
```json
{
"action": "mute",
"target": "username-or-id",
"moderator": "admin",
"reason": "spam",
"room": "main"
}
```
### Analytics
- GET /api/admin/analytics
Returns totals (messages, moderation actions, rooms) and top room summary.
### System
- GET /api/admin/system
Returns service health snapshot for chat/playout/redis/database and config metadata.
### Active Users
- GET /api/admin/users
Returns currently discovered presence users (room + username) when Redis is available.
## Media Library API
### Media Collection
| Method & Path | Description |
| ------------------------------ | ------------------------------------------------- |
| `GET /api/admin/media` | List media |
| `POST /api/admin/media` | Multipart upload (legacy), form field name `file` |
| `GET /api/admin/media/{id}` | Get item |
| `PUT /api/admin/media/{id}` | Update item |
| `DELETE /api/admin/media/{id}` | Delete item |
- GET /api/admin/media
- POST /api/admin/media (multipart upload, legacy path)
Multipart form field name: file
### Media Item
- GET /api/admin/media/{id}
- PUT /api/admin/media/{id}
- DELETE /api/admin/media/{id}
PUT request:
PUT media body:
```json
{
"status": "ready",
"title": "My Clip",
"tags": "music,live"
}
{ "status": "ready", "title": "My Clip", "tags": "music,live" }
```
## Resumable Upload API
Maximum file size: 2GB
Max file size: **2GB** · Default chunk size: **8MB**
Default chunk size: 8MB
| Method & Path | Description |
| ------------------------------------------------------------------ | ----------------------------------------------------------------- |
| `POST /api/admin/media/uploads` | Initialize or resume session |
| `GET /api/admin/media/uploads/{uploadId}` | Check upload status |
| `PUT /api/admin/media/uploads/{uploadId}/chunk?index={chunkIndex}` | Upload chunk (`Content-Type: application/octet-stream`) |
| `POST /api/admin/media/uploads/{uploadId}/complete` | Complete upload → returns media listing (`files` + `generatedAt`) |
### Initialize or Resume Session
- POST /api/admin/media/uploads
Request:
Initialize request:
```json
{
@@ -223,7 +108,7 @@ Request:
}
```
Response:
Status envelope (returned by init, status check, and chunk upload):
```json
{
@@ -237,61 +122,33 @@ Response:
}
```
### Check Upload Status
## Metrics & Health
- GET /api/admin/media/uploads/{uploadId}
Returns same status envelope as initialize.
### Upload Chunk
- PUT /api/admin/media/uploads/{uploadId}/chunk?index={chunkIndex}
- Content-Type: application/octet-stream
Response returns updated upload status envelope.
### Complete Upload
- POST /api/admin/media/uploads/{uploadId}/complete
On success returns admin media listing payload (files + generatedAt).
## Metrics and Health
### Chat metrics
- GET /metrics
Prometheus exposition includes connected clients, stored messages, moderation actions, Redis indicators, active admin sessions, and build info.
### Health
- GET /health on chat service
- GET /health on playout service
- GET /health on nginx reverse proxy
- `GET /metrics` (chat) — Prometheus: connected clients, stored messages, moderation actions, Redis indicators, active admin sessions, build info
- `GET /health` — chat service, playout service, and nginx reverse proxy each expose it
## HTTP Status Conventions
| Code | Meaning | When |
| ---- | --------------------- | ----------------------------------------------------------- |
| 200 | OK | Successful GET, PUT, or action |
| 200 | OK | Successful GET, PUT, action |
| 201 | Created | Successful POST (upload init, override created) |
| 204 | No Content | Successful DELETE, CORS preflight |
| 400 | Bad Request | Malformed JSON, missing required fields, invalid input |
| 401 | Unauthorized | Missing or expired admin session |
| 403 | Forbidden | Valid session but insufficient permissions |
| 401 | Unauthorized | Missing/expired admin session |
| 403 | Forbidden | Valid session, insufficient permissions |
| 404 | Not Found | Resource doesn't exist |
| 405 | Method Not Allowed | Wrong HTTP method for endpoint |
| 405 | Method Not Allowed | Wrong method for endpoint |
| 409 | Conflict | Schedule override overlap, upload ID mismatch |
| 413 | Payload Too Large | Upload exceeds 2 GB limit |
| 422 | Unprocessable | File fails normalization check (codec, fps, resolution) |
| 429 | Too Many Requests | Rate limit exceeded (chat messages, WS connections, login) |
| 429 | Too Many Requests | Rate limit exceeded (chat, WS, login) |
| 500 | Internal Server Error | Unexpected server failure |
| 503 | Service Unavailable | Redis unavailable for user actions, playout health degraded |
## Rate Limiting
All limits are per-IP (from `X-Forwarded-For` or `RemoteAddr`) unless noted.
Per-IP (from `X-Forwarded-For` or `RemoteAddr`) unless noted.
| Resource | Limit | Window | Response |
| --------------------- | ---------------------------------- | ---------------------------------------- | ------------------------------------------------------ |
@@ -299,9 +156,4 @@ All limits are per-IP (from `X-Forwarded-For` or `RemoteAddr`) unless noted.
| WebSocket connections | 5 | 60s | 429 + `"too many connections from this IP"` |
| Admin login | 5 | 5min | 429 + `"too many login attempts, try again later"` |
Rate limit headers on applicable responses:
- `X-RateLimit-Limit`: maximum requests per window
- `Retry-After`: seconds until window resets
- 413: payload too large
- 500: internal server error
Rate limit headers (applicable responses): `X-RateLimit-Limit` (max requests/window), `Retry-After` (seconds until reset). 413 = payload too large; 500 = internal server error.