167 lines
7.5 KiB
Markdown
167 lines
7.5 KiB
Markdown
# HTTP API Reference
|
|
|
|
## Endpoints
|
|
|
|
### GET /api/health
|
|
- **Description:** Health check endpoint.
|
|
- **Auth:** None
|
|
- **Query params:** None
|
|
- **Response:** `{"status":"ok"}`
|
|
|
|
---
|
|
|
|
### GET /api/content/:cxid
|
|
- **Description:** Get content metadata (files, view limits, password status, etc.).
|
|
- **Auth:**
|
|
- None if the content has no password.
|
|
- Password required if the content has a password. Provide via query param `sc` **or** cookie `cgcx_pw`.
|
|
- **Path params:**
|
|
- `cxid` — Content ID string.
|
|
- **Query params:**
|
|
- `sc` (optional) — Password as a query string parameter.
|
|
- **Response formats:**
|
|
- `200 OK` — JSON metadata object:
|
|
```json
|
|
{
|
|
"cxid": "string",
|
|
"files": [
|
|
{
|
|
"idx": 0,
|
|
"name": "string",
|
|
"mime": "string",
|
|
"size": 12345,
|
|
"render_flags": 0
|
|
}
|
|
],
|
|
"has_password": true,
|
|
"max_views": 10,
|
|
"current_views": 3,
|
|
"allow_download": true,
|
|
"created_at": "2024-01-01T00:00:00+00:00",
|
|
"total_size": 1234567,
|
|
"author": {
|
|
"username": "@example",
|
|
"user_id": 123456789
|
|
}
|
|
}
|
|
```
|
|
- `401 Unauthorized` — Password required but missing or invalid.
|
|
- `404 Not Found` — Content does not exist or has been deleted/blacklisted.
|
|
- `410 Gone` — Content has reached its maximum view count.
|
|
- **Notes:**
|
|
- `total_size` is the sum of all file sizes in bytes.
|
|
- `author` is gated by the content's `show_author` flag. It is `null` when `show_author = false`, when the uploader's user record is missing, or when the user has no Telegram username (`username` may be `null` even when `user_id` is present).
|
|
- If authentication succeeds via the `sc` query parameter, the server sets an HMAC-signed `cgcx_pw` cookie on the response (`Max-Age=3600; SameSite=Strict; HttpOnly; Path=/`).
|
|
|
|
---
|
|
|
|
### GET /api/content/:cxid/file/:file_idx
|
|
- **Description:** Serve a decrypted file. Supports HTTP Range requests for video/audio streaming. Returns the file with `Content-Disposition: inline` by default, or `attachment` when downloading.
|
|
- **Auth:**
|
|
- None if the content has no password.
|
|
- Password required if the content has a password. Provide via query param `sc` **or** cookie `cgcx_pw`.
|
|
- **Path params:**
|
|
- `cxid` — Content ID string.
|
|
- `file_idx` — Zero-based file index within the content bundle.
|
|
- **Query params:**
|
|
- `sc` (optional) — Password as a query string parameter.
|
|
- `download` (optional) — If truthy (`1`, `true`, `yes`), requests a download (`Content-Disposition: attachment`). Ignored if `allow_download` is `false` for the content.
|
|
- **Response formats:**
|
|
- `200 OK` — File stream with appropriate `Content-Type`.
|
|
- `206 Partial Content` — Byte-range response (if `Range` header is present and valid).
|
|
- `401 Unauthorized` — Password required but missing or invalid.
|
|
- `403 Forbidden` — Download requested but not allowed, or path traversal blocked.
|
|
- `404 Not Found` — Content or file index does not exist, or content deleted/blacklisted.
|
|
- `410 Gone` — Content has reached its maximum view count.
|
|
- `416 Range Not Satisfiable` — Invalid `Range` header.
|
|
- **Notes:**
|
|
- The server increments the view counter on successful full-file responses. Range requests, `If-None-Match` (ETag) matches, and HEAD requests do **not** increment the counter.
|
|
- If the incremented view count reaches `max_views`, the server may delete content files (depending on `keep_content` config) and mark the content as `Deleted`, returning `410 Gone`.
|
|
- `Accept-Ranges: bytes` is included for `video/*` and `audio/*` MIME types.
|
|
- Cache-Control is `private, max-age=60` for unprotected content and `private, no-store, max-age=0` for password-protected content.
|
|
- If authentication succeeds via the `sc` query parameter, the server sets an HMAC-signed `cgcx_pw` cookie on the response.
|
|
|
|
---
|
|
|
|
### GET /api/content/:cxid/file/:file_idx/raw
|
|
- **Description:** Serve the fully decrypted file as raw plain text (`text/plain; charset=utf-8`). The entire file is decrypted into memory before being returned. No Range support.
|
|
- **Auth:**
|
|
- None if the content has no password.
|
|
- Password required if the content has a password. Provide via query param `sc` **or** cookie `cgcx_pw`.
|
|
- **Path params:**
|
|
- `cxid` — Content ID string.
|
|
- `file_idx` — Zero-based file index within the content bundle.
|
|
- **Query params:**
|
|
- `sc` (optional) — Password as a query string parameter.
|
|
- **Response formats:**
|
|
- `200 OK` — Plain text body.
|
|
- `401 Unauthorized` — Password required but missing or invalid.
|
|
- `403 Forbidden` — Path traversal blocked.
|
|
- `404 Not Found` — Content or file index does not exist, or content deleted/blacklisted.
|
|
- `410 Gone` — Content has reached its maximum view count.
|
|
- **Notes:**
|
|
- The server performs BLAKE3 integrity verification after full decryption.
|
|
- If authentication succeeds via the `sc` query parameter, the server sets an HMAC-signed `cgcx_pw` cookie on the response.
|
|
|
|
---
|
|
|
|
### POST /api/content/:cxid/report
|
|
- **Description:** Report content for review. Creates a report record and forwards a notification to all configured review groups via the Telegram Bot API.
|
|
- **Auth:** None
|
|
- **Path params:**
|
|
- `cxid` — Content ID string.
|
|
- **Body:** JSON object:
|
|
```json
|
|
{
|
|
"reason": "string"
|
|
}
|
|
```
|
|
- **Response formats:**
|
|
- `204 No Content` — Report accepted.
|
|
- `404 Not Found` — Content does not exist or has been deleted/blacklisted.
|
|
- **Notes:**
|
|
- The report is recorded with `reporter_user_id = 0` to indicate a web-based report.
|
|
- Rate limiting is covered by the general API governor.
|
|
|
|
---
|
|
|
|
### POST /api/content/:cxid/verify-password
|
|
- **Description:** Explicitly verify a password for password-protected content and receive an authentication cookie.
|
|
- **Auth:** None (this is the endpoint used to *obtain* auth).
|
|
- **Path params:**
|
|
- `cxid` — Content ID string.
|
|
- **Body:** JSON object:
|
|
```json
|
|
{
|
|
"password": "string"
|
|
}
|
|
```
|
|
- **Response formats:**
|
|
- `204 No Content` — Password is correct. The response includes a `Set-Cookie` header with `cgcx_pw`.
|
|
- `401 Unauthorized` — Password is incorrect.
|
|
- `404 Not Found` — Content does not exist.
|
|
- **Notes:**
|
|
- If the content has no password, the endpoint returns `204 No Content` without setting a cookie.
|
|
- This endpoint has a separate, stricter rate limit than the general API.
|
|
|
|
---
|
|
|
|
## General Behavior
|
|
|
|
### CORS
|
|
The server allows cross-origin requests from its configured `base_url` and common local development origins (`http://127.0.0.1:5173`, `http://localhost:5173`, `http://127.0.0.1:8090`, `http://localhost:8090`).
|
|
|
|
### Rate Limiting
|
|
- General API routes (`/api/health`, `/api/content/...`) share a per-IP rate limit configured by `requests_per_minute` and `burst`.
|
|
- `POST /api/content/:cxid/verify-password` has its own rate limit with a burst of 3 and a separate `password_attempts_per_minute` setting.
|
|
|
|
### Password Flow
|
|
- The `sc` query parameter is checked on both the metadata endpoint (`GET /api/content/:cxid`) and the file endpoints (`GET /api/content/:cxid/file/:file_idx`, `GET /api/content/:cxid/file/:file_idx/raw`). When valid, the server sets an HMAC-signed `cgcx_pw` cookie on the response.
|
|
- Passwords can also be provided via the `cgcx_pw` cookie.
|
|
- For programmatic verification, use `POST /api/content/:cxid/verify-password`.
|
|
|
|
### Fallback / Static Assets
|
|
- `/assets/*` — Serves static files from `frontend/dist/assets`.
|
|
- All other non-`/api` paths — Serves `frontend/dist/index.html` (SPA fallback).
|
|
- `/api/*` paths with no matching route — Return `404 Not Found` JSON.
|