feat: update documentation with project details, deployment instructions, and database concurrency management
Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
@@ -4,6 +4,14 @@ Describes crosscutting concepts (practices, patterns, regulations or solution id
|
||||
|
||||
> Pick **only** the most-needed topics for your system.
|
||||
|
||||
## OpenRouter API Integration
|
||||
|
||||
see [docs/8.1-openrouter.md](./8.1-openrouter.md) for details on how the backend integrates with OpenRouter for multi-modal AI generation, including image and video generation flows.
|
||||
|
||||
## DuckDB Concurrency and Storage
|
||||
|
||||
See [docs/8.2-duckdb.md](./8.2-duckdb.md) for details on how the backend handles concurrent access to DuckDB and manages the database file on the host filesystem.
|
||||
|
||||
## Security
|
||||
|
||||
- All API endpoints (except `/auth/login`) require a valid JWT in the `Authorization: Bearer` header.
|
||||
@@ -25,72 +33,3 @@ Describes crosscutting concepts (practices, patterns, regulations or solution id
|
||||
|
||||
- All secrets (API keys, DB path, JWT secret) loaded from environment variables or `.env` file.
|
||||
- No secrets committed to source control.
|
||||
|
||||
## DuckDB Concurrency and Storage
|
||||
|
||||
### Single Writer Per Process
|
||||
|
||||
DuckDB allows only one process to open the database file in read-write mode at a time. The FastAPI backend must be run with a single worker (`uvicorn --workers 1`). Running multiple workers against the same DuckDB file will cause startup errors.
|
||||
|
||||
### asyncio.Lock for Writes
|
||||
|
||||
All database write operations (`INSERT`, `UPDATE`, `DELETE`) in the FastAPI async context are wrapped in a single `asyncio.Lock` (`get_write_lock()` from `backend/app/db.py`). This prevents concurrent coroutines from issuing overlapping writes within the single process, which would otherwise raise DuckDB optimistic concurrency errors.
|
||||
|
||||
Read operations (`SELECT`) do not require the lock — DuckDB's MVCC provides consistent read snapshots.
|
||||
|
||||
### Schema
|
||||
|
||||
```sql
|
||||
CREATE TABLE users (
|
||||
id UUID DEFAULT uuid() PRIMARY KEY,
|
||||
email VARCHAR NOT NULL UNIQUE,
|
||||
password_hash VARCHAR NOT NULL,
|
||||
role VARCHAR DEFAULT 'user',
|
||||
created_at TIMESTAMP DEFAULT now(),
|
||||
updated_at TIMESTAMP DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE TABLE refresh_tokens (
|
||||
jti UUID DEFAULT uuid() PRIMARY KEY,
|
||||
user_id UUID NOT NULL, -- soft FK to users.id
|
||||
issued_at TIMESTAMP DEFAULT now(),
|
||||
expires_at TIMESTAMP NOT NULL,
|
||||
revoked BOOLEAN DEFAULT false
|
||||
);
|
||||
```
|
||||
|
||||
> The `REFERENCES users(id)` foreign key is intentionally omitted from `refresh_tokens`. DuckDB fires FK checks on `UPDATE` of the parent table (including email changes), causing false constraint violations. Referential integrity is enforced manually: deleting a user also deletes their refresh tokens in the same write transaction.
|
||||
|
||||
### Access Tokens
|
||||
|
||||
Access tokens are **stateless** JWTs — not stored in the database. They are validated by signature and expiry claim only. The short TTL (15 minutes) limits the blast radius if a token is leaked.
|
||||
|
||||
### Refresh Tokens
|
||||
|
||||
Refresh tokens store a JTI (JWT ID) UUID in the `refresh_tokens` table. On each use the old JTI is revoked and a new one issued (rotation). On logout the JTI is immediately revoked. Expired and revoked tokens can be purged via `POST /admin/tokens/purge`.
|
||||
|
||||
### Future: AI Generation History
|
||||
|
||||
AI generation metadata (model, prompt, cost, result URLs) can be stored as JSON columns in a future `generation_history` table in DuckDB, enabling per-user analytics and usage dashboards at zero extra infrastructure cost.
|
||||
|
||||
## OpenRouter API Integration
|
||||
|
||||
### Image Generation
|
||||
|
||||
Image generation uses two different OpenRouter endpoints depending on the model:
|
||||
|
||||
- **Legacy endpoint** (`/images/generations`): Used by DALL-E 3 and similar models. Returns `data[].url` and `data[].b64_json`.
|
||||
- **Chat completions** (`/chat/completions` with `modalities: ["image"]`): Used by FLUX.2 Klein 4B and GPT-5 Image Mini. Returns `choices[0].message.images[].image_url.url` as base64 data URLs.
|
||||
|
||||
The router auto-detects the model type and routes accordingly. Image configuration (`aspect_ratio`, `image_size`) is passed via `image_config` for chat-based models.
|
||||
|
||||
### Video Generation
|
||||
|
||||
Video generation uses OpenRouter's `/api/v1/videos` endpoint with a **submit-and-poll** pattern:
|
||||
|
||||
1. `POST /api/v1/videos` with `model`, `prompt`, `aspect_ratio`, `resolution`, `duration_seconds`
|
||||
2. Response: `{"id": "job_id", "polling_url": "https://..."}` with `status: "queued"`
|
||||
3. Poll `GET polling_url` every 5 seconds until `status` is `"completed"` or `"failed"`
|
||||
4. Completed response includes `unsigned_urls: [str]` array with video download URLs
|
||||
|
||||
Supported models: `openai/sora-2-pro`, `google/veo-3.1-fast`. Both text-to-video and image-to-video use the same `/api/v1/videos` endpoint (image-to-video includes `image_url` in the request body).
|
||||
|
||||
Reference in New Issue
Block a user