90ac0b81d1
Add full V2 architecture: identity, content, studio (.NET 10) and file, render, notification, gateway (Go) services with vendored deps, plus DB migrations, event/API contracts, and an init-db script. Wire the Next.js frontend to the gateway: server-side JWT auth routes (login/register/refresh/logout/me), gateway fetch helper, and session/ cookie/jwt helpers under src/lib. Containerize the stack via docker-compose.v2.yml and per-service Dockerfiles. Base images resolve through a Nexus mirror (Docker Hub) and MCR directly; npm/NuGet pull from Nexus groups. Self-host fonts via next/font/local to avoid Google Fonts (geo-blocked). Add CI workflow and ignore .env.v2, *.stackdump, and .NET bin/obj. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
4.1 KiB
4.1 KiB
WebSocket Protocol — Render Progress
Live render progress is pushed to the browser via WebSocket.
Connection
URL: wss://api.flatrender.ir/ws/v1/render/{job_id}?token={jwt}
Headers: Sec-WebSocket-Protocol: flatrender.v1
The JWT carries user_id + tenant_id. The Gateway validates that the
caller owns the job_id. Connection is closed with code 4403 if not.
Server → Client messages
All server messages are JSON with type discriminator.
hello — sent on connect
{
"type": "hello",
"job_id": "uuid",
"current_state": {
"step": "Rendering",
"progress": 47,
"current_frame": 470,
"total_frames": 720,
"started_at": "2026-05-27T10:00:00Z"
}
}
progress — frequent (max every 500ms)
{
"type": "progress",
"job_id": "uuid",
"step": "Rendering",
"progress": 67,
"current_frame": 482,
"total_frames": 720,
"eta_seconds": 45,
"preview_b64": "data:image/jpeg;base64,/9j/4AAQ...",
"active_nodes": 3,
"message": null
}
preview_b64 is sent at most every 2s (last rendered frame thumbnail,
~5-15 KB).
step_change — pipeline transition
{
"type": "step_change",
"job_id": "uuid",
"from_step": "Rendering",
"to_step": "Validating",
"at": "2026-05-27T10:05:00Z"
}
frame_repair — frames being re-rendered
{
"type": "frame_repair",
"job_id": "uuid",
"missing_frames": [347, 348, 521],
"corrupt_frames": [402],
"attempt": 1,
"max_attempts": 3
}
node_event — node-related notice
{
"type": "node_event",
"job_id": "uuid",
"event": "node_crashed",
"node_id": "uuid",
"auto_recovered": true,
"message": "AE crashed on node-7, work reassigned"
}
done — terminal success
{
"type": "done",
"job_id": "uuid",
"export_id": "uuid",
"output_url": "https://cdn.flatrender.ir/exports/abc.mp4",
"thumbnail_url": "https://cdn.flatrender.ir/exports/abc.jpg",
"duration_sec": 30,
"size_bytes": 14523456,
"compute_seconds": 124
}
failed — terminal failure
{
"type": "failed",
"job_id": "uuid",
"failed_at_step": "Rendering",
"error_message": "AE crashed too many times on this template",
"error_code": "AE_REPEATED_CRASH",
"refund_issued": true,
"trace_id": "uuid"
}
cancelled — terminal cancellation
{
"type": "cancelled",
"job_id": "uuid",
"cancelled_at": "2026-05-27T10:03:12Z",
"progress_when_cancelled": 42
}
error — protocol-level error (kept open)
{
"type": "error",
"code": "RATE_LIMIT",
"message": "Too many messages; slow down"
}
ping — keepalive
{ "type": "ping", "t": 1714294800 }
Client SHOULD respond with {"type":"pong","t":1714294800} within 30s
or the server may close the connection.
Client → Server messages
pong
{ "type": "pong", "t": 1714294800 }
subscribe_snapshot — also get scene snapshot updates over same socket
{ "type": "subscribe_snapshot", "snapshot_id": "uuid" }
cancel_job — request cancellation
{ "type": "cancel_job", "job_id": "uuid" }
The server replies with a cancelled message once accepted.
Close codes
| Code | Meaning |
|---|---|
| 1000 | Normal close (job completed/failed) |
| 1001 | Server going away (deploy) |
| 1008 | Policy violation (bad message) |
| 4401 | Unauthorized (bad/expired JWT) |
| 4403 | Forbidden (don't own this job) |
| 4404 | Job not found |
| 4429 | Rate limited |
Reconnect strategy (client)
- Reconnect with exponential backoff (1s, 2s, 4s, 8s, max 30s)
- On reconnect,
hellocarriescurrent_stateso UI catches up - WebSocket is best-effort; UI should also poll
GET /v1/renders/{id}if it hasn't receivedprogressin > 15s
Rate limits
| Direction | Limit |
|---|---|
Server → Client progress |
Max 2 Hz |
| Server → Client total messages | 10 per second |
| Client → Server | 5 per second |