feat: V2 microservices stack — backend services, gateway, JWT auth
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>
This commit is contained in:
@@ -0,0 +1,823 @@
|
||||
openapi: 3.0.3
|
||||
info:
|
||||
title: FlatRender Render Orchestrator (internal)
|
||||
version: 1.0.0
|
||||
description: |
|
||||
Render job orchestration, node pool management, snapshots, exports.
|
||||
Owned by Go service. The browser connects to WebSocket via Gateway
|
||||
for live progress.
|
||||
|
||||
servers:
|
||||
- url: http://render-svc.internal/v1
|
||||
|
||||
security:
|
||||
- BearerAuth: []
|
||||
- ServiceToken: []
|
||||
|
||||
tags:
|
||||
- name: Jobs
|
||||
- name: Snapshots
|
||||
- name: Exports
|
||||
- name: Nodes
|
||||
- name: Admin
|
||||
- name: Internal
|
||||
|
||||
paths:
|
||||
|
||||
# ===================== JOBS =====================
|
||||
/renders:
|
||||
get:
|
||||
tags: [Jobs]
|
||||
summary: List user's render jobs
|
||||
parameters:
|
||||
- { name: status, in: query, schema: { type: string } }
|
||||
- { name: page, in: query, schema: { type: integer } }
|
||||
- { name: page_size, in: query, schema: { type: integer } }
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
data: { type: array, items: { $ref: '#/components/schemas/RenderJob' } }
|
||||
meta: { $ref: '#/components/schemas/PaginationMeta' }
|
||||
post:
|
||||
tags: [Jobs]
|
||||
summary: Submit new render job
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema: { $ref: '#/components/schemas/RenderJobCreate' }
|
||||
responses:
|
||||
'201':
|
||||
content:
|
||||
application/json:
|
||||
schema: { $ref: '#/components/schemas/RenderJob' }
|
||||
'402': { description: Insufficient balance/charge }
|
||||
|
||||
/renders/{job_id}:
|
||||
get:
|
||||
tags: [Jobs]
|
||||
parameters:
|
||||
- { name: job_id, in: path, required: true, schema: { type: string, format: uuid } }
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema: { $ref: '#/components/schemas/RenderJobDetail' }
|
||||
|
||||
/renders/{job_id}/cancel:
|
||||
post:
|
||||
tags: [Jobs]
|
||||
parameters:
|
||||
- { name: job_id, in: path, required: true, schema: { type: string, format: uuid } }
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
reason: { type: string }
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
cancelled: { type: boolean }
|
||||
refund_amount_minor: { type: integer, format: int64 }
|
||||
|
||||
/renders/{job_id}/retry:
|
||||
post:
|
||||
tags: [Jobs]
|
||||
summary: Retry a failed render (uses same config)
|
||||
parameters:
|
||||
- { name: job_id, in: path, required: true, schema: { type: string, format: uuid } }
|
||||
responses:
|
||||
'201':
|
||||
content:
|
||||
application/json:
|
||||
schema: { $ref: '#/components/schemas/RenderJob' }
|
||||
|
||||
/renders/{job_id}/progress:
|
||||
get:
|
||||
tags: [Jobs]
|
||||
summary: |
|
||||
Fallback polling endpoint when WebSocket isn't usable.
|
||||
Browser primary is WS at /ws/v1/render/{job_id}.
|
||||
parameters:
|
||||
- { name: job_id, in: path, required: true, schema: { type: string, format: uuid } }
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema: { $ref: '#/components/schemas/RenderProgress' }
|
||||
|
||||
/renders/{job_id}/logs:
|
||||
get:
|
||||
tags: [Jobs]
|
||||
summary: Get render execution logs (admin or owner)
|
||||
parameters:
|
||||
- { name: job_id, in: path, required: true, schema: { type: string, format: uuid } }
|
||||
- { name: level, in: query, schema: { type: string, enum: [debug, info, warn, error] } }
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
logs:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
timestamp: { type: string, format: date-time }
|
||||
level: { type: string }
|
||||
node_id: { type: string, format: uuid, nullable: true }
|
||||
message: { type: string }
|
||||
meta: { type: object }
|
||||
|
||||
# ===================== SNAPSHOTS =====================
|
||||
/snapshots:
|
||||
post:
|
||||
tags: [Snapshots]
|
||||
summary: Request single-frame snapshot of a scene
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
required: [saved_project_id, scene_key, frame_number]
|
||||
properties:
|
||||
saved_project_id: { type: string, format: uuid }
|
||||
scene_key: { type: string }
|
||||
frame_number: { type: integer, minimum: 0 }
|
||||
responses:
|
||||
'202':
|
||||
description: Snapshot queued (or returned immediately if cached)
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
snapshot_id: { type: string, format: uuid }
|
||||
status: { type: string, enum: [Cached, Pending, Rendering] }
|
||||
image_url: { type: string, nullable: true }
|
||||
thumbnail_url: { type: string, nullable: true }
|
||||
expires_at: { type: string, format: date-time, nullable: true }
|
||||
|
||||
/snapshots/{snapshot_id}:
|
||||
get:
|
||||
tags: [Snapshots]
|
||||
parameters:
|
||||
- { name: snapshot_id, in: path, required: true, schema: { type: string, format: uuid } }
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema: { $ref: '#/components/schemas/Snapshot' }
|
||||
|
||||
# ===================== EXPORTS =====================
|
||||
/exports:
|
||||
get:
|
||||
tags: [Exports]
|
||||
summary: List user's exports
|
||||
parameters:
|
||||
- { name: page, in: query, schema: { type: integer } }
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
data: { type: array, items: { $ref: '#/components/schemas/Export' } }
|
||||
meta: { $ref: '#/components/schemas/PaginationMeta' }
|
||||
|
||||
/exports/{export_id}:
|
||||
get:
|
||||
tags: [Exports]
|
||||
parameters:
|
||||
- { name: export_id, in: path, required: true, schema: { type: string, format: uuid } }
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema: { $ref: '#/components/schemas/ExportDetail' }
|
||||
delete:
|
||||
tags: [Exports]
|
||||
parameters:
|
||||
- { name: export_id, in: path, required: true, schema: { type: string, format: uuid } }
|
||||
responses:
|
||||
'204': { description: Deleted }
|
||||
|
||||
/exports/{export_id}/extend:
|
||||
post:
|
||||
tags: [Exports]
|
||||
summary: Extend auto-delete date (paid feature)
|
||||
parameters:
|
||||
- { name: export_id, in: path, required: true, schema: { type: string, format: uuid } }
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
days: { type: integer, minimum: 1, maximum: 365 }
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
new_auto_delete_date: { type: string, format: date-time }
|
||||
|
||||
/exports/{export_id}/download-url:
|
||||
get:
|
||||
tags: [Exports]
|
||||
summary: Get presigned MinIO URL (short-lived)
|
||||
parameters:
|
||||
- { name: export_id, in: path, required: true, schema: { type: string, format: uuid } }
|
||||
- { name: format, in: query, schema: { type: string, default: mp4 } }
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
url: { type: string }
|
||||
expires_at: { type: string, format: date-time }
|
||||
|
||||
# ===================== NODES (admin) =====================
|
||||
/nodes:
|
||||
get:
|
||||
tags: [Nodes]
|
||||
summary: List nodes
|
||||
parameters:
|
||||
- { name: region, in: query, schema: { type: string } }
|
||||
- { name: status, in: query, schema: { type: string } }
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
data: { type: array, items: { $ref: '#/components/schemas/RenderNode' } }
|
||||
post:
|
||||
tags: [Nodes]
|
||||
summary: (Admin) Register new node
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema: { $ref: '#/components/schemas/RenderNodeCreate' }
|
||||
responses:
|
||||
'201':
|
||||
content:
|
||||
application/json:
|
||||
schema: { $ref: '#/components/schemas/RenderNode' }
|
||||
|
||||
/nodes/{node_id}:
|
||||
get:
|
||||
tags: [Nodes]
|
||||
parameters:
|
||||
- { name: node_id, in: path, required: true, schema: { type: string, format: uuid } }
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema: { $ref: '#/components/schemas/RenderNodeDetail' }
|
||||
patch:
|
||||
tags: [Nodes]
|
||||
parameters:
|
||||
- { name: node_id, in: path, required: true, schema: { type: string, format: uuid } }
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
priority: { type: integer }
|
||||
is_active: { type: boolean }
|
||||
accepts_new_jobs: { type: boolean }
|
||||
node_kind: { type: string }
|
||||
owner_user_id: { type: string, format: uuid }
|
||||
next_maintenance_at: { type: string, format: date-time }
|
||||
maintenance_reason: { type: string }
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema: { $ref: '#/components/schemas/RenderNode' }
|
||||
|
||||
/nodes/{node_id}/restart:
|
||||
post:
|
||||
tags: [Nodes]
|
||||
summary: Reboot node OS
|
||||
parameters:
|
||||
- { name: node_id, in: path, required: true, schema: { type: string, format: uuid } }
|
||||
responses:
|
||||
'202': { description: Restart issued }
|
||||
|
||||
/nodes/{node_id}/release:
|
||||
post:
|
||||
tags: [Nodes]
|
||||
summary: Force-release node from any current job
|
||||
parameters:
|
||||
- { name: node_id, in: path, required: true, schema: { type: string, format: uuid } }
|
||||
responses:
|
||||
'204': { description: Released }
|
||||
|
||||
/nodes/{node_id}/close-ae:
|
||||
post:
|
||||
tags: [Nodes]
|
||||
summary: Force-kill AfterFX on a node
|
||||
parameters:
|
||||
- { name: node_id, in: path, required: true, schema: { type: string, format: uuid } }
|
||||
responses:
|
||||
'204': { description: AE closed }
|
||||
|
||||
/nodes/{node_id}/health:
|
||||
get:
|
||||
tags: [Nodes]
|
||||
summary: Current node health
|
||||
parameters:
|
||||
- { name: node_id, in: path, required: true, schema: { type: string, format: uuid } }
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema: { $ref: '#/components/schemas/NodeHealth' }
|
||||
|
||||
/nodes/{node_id}/health/history:
|
||||
get:
|
||||
tags: [Nodes]
|
||||
parameters:
|
||||
- { name: node_id, in: path, required: true, schema: { type: string, format: uuid } }
|
||||
- { name: from, in: query, schema: { type: string, format: date-time } }
|
||||
- { name: to, in: query, schema: { type: string, format: date-time } }
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
data:
|
||||
type: array
|
||||
items: { $ref: '#/components/schemas/NodeHealth' }
|
||||
|
||||
/nodes/{node_id}/crashes:
|
||||
get:
|
||||
tags: [Nodes]
|
||||
parameters:
|
||||
- { name: node_id, in: path, required: true, schema: { type: string, format: uuid } }
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
data: { type: array, items: { $ref: '#/components/schemas/NodeCrash' } }
|
||||
|
||||
/node-updates:
|
||||
get:
|
||||
tags: [Nodes]
|
||||
summary: Available node software updates
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
data: { type: array, items: { $ref: '#/components/schemas/NodeUpdate' } }
|
||||
|
||||
/node-updates/{update_id}/rollout:
|
||||
post:
|
||||
tags: [Nodes]
|
||||
summary: Trigger rollout to selected nodes
|
||||
parameters:
|
||||
- { name: update_id, in: path, required: true, schema: { type: string, format: uuid } }
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
node_ids: { type: array, items: { type: string, format: uuid } }
|
||||
responses:
|
||||
'202': { description: Rollout queued }
|
||||
|
||||
# ===================== INTERNAL (called by node agents) =====================
|
||||
/internal/nodes/{node_id}/heartbeat:
|
||||
post:
|
||||
tags: [Internal]
|
||||
summary: Node sends heartbeat (every 5s)
|
||||
security: [NodeHmac: []]
|
||||
parameters:
|
||||
- { name: node_id, in: path, required: true, schema: { type: string, format: uuid } }
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema: { $ref: '#/components/schemas/NodeHeartbeat' }
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
next_heartbeat_in_sec: { type: integer }
|
||||
pending_commands:
|
||||
type: array
|
||||
description: e.g. "cancel current job", "update software"
|
||||
items: { type: object }
|
||||
|
||||
/internal/nodes/{node_id}/online:
|
||||
post:
|
||||
tags: [Internal]
|
||||
summary: Node agent reports it just started
|
||||
security: [NodeHmac: []]
|
||||
parameters:
|
||||
- { name: node_id, in: path, required: true, schema: { type: string, format: uuid } }
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
node_agent_version: { type: string }
|
||||
current_ae_version: { type: string }
|
||||
available_ae_versions: { type: array, items: { type: string } }
|
||||
ram_gb: { type: integer }
|
||||
cpu_cores: { type: integer }
|
||||
cache_used_gb: { type: integer }
|
||||
cached_template_md5s: { type: array, items: { type: string } }
|
||||
responses:
|
||||
'200': { description: Acknowledged }
|
||||
|
||||
/internal/render/jobs/{job_id}/frames:
|
||||
post:
|
||||
tags: [Internal]
|
||||
summary: Node pushes per-frame progress
|
||||
security: [NodeHmac: []]
|
||||
parameters:
|
||||
- { name: job_id, in: path, required: true, schema: { type: string, format: uuid } }
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
required: [frame_job_id, frame_number]
|
||||
properties:
|
||||
frame_job_id: { type: string, format: uuid }
|
||||
frame_number: { type: integer }
|
||||
file_size_bytes: { type: integer }
|
||||
completed_at: { type: string, format: date-time }
|
||||
responses:
|
||||
'204': { description: Recorded }
|
||||
|
||||
/internal/render/jobs/{job_id}/crash:
|
||||
post:
|
||||
tags: [Internal]
|
||||
summary: Node reports an AE crash on this job
|
||||
security: [NodeHmac: []]
|
||||
parameters:
|
||||
- { name: job_id, in: path, required: true, schema: { type: string, format: uuid } }
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
required: [node_id]
|
||||
properties:
|
||||
node_id: { type: string, format: uuid }
|
||||
frame_job_id: { type: string, format: uuid }
|
||||
last_known_frame: { type: integer }
|
||||
crash_signal: { type: string }
|
||||
ae_version: { type: string }
|
||||
error_log_tail: { type: string }
|
||||
log_file_url: { type: string }
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
action_recommended:
|
||||
type: string
|
||||
enum: [ResetAndRestart, ReassignWork, RestartNode]
|
||||
reassigned_to_node_id:
|
||||
type: string
|
||||
format: uuid
|
||||
nullable: true
|
||||
|
||||
/internal/render/jobs/{job_id}/replica-ready:
|
||||
post:
|
||||
tags: [Internal]
|
||||
summary: Node reports replica .aep saved (after JSX run)
|
||||
security: [NodeHmac: []]
|
||||
parameters:
|
||||
- { name: job_id, in: path, required: true, schema: { type: string, format: uuid } }
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
required: [node_id, replica_path]
|
||||
properties:
|
||||
node_id: { type: string, format: uuid }
|
||||
replica_path: { type: string }
|
||||
replica_md5: { type: string }
|
||||
responses:
|
||||
'204': { description: Acknowledged }
|
||||
|
||||
/internal/nodes/{node_id}/cache-update:
|
||||
post:
|
||||
tags: [Internal]
|
||||
summary: Node reports a template cache change
|
||||
security: [NodeHmac: []]
|
||||
parameters:
|
||||
- { name: node_id, in: path, required: true, schema: { type: string, format: uuid } }
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
required: [action, aep_file_md5]
|
||||
properties:
|
||||
action: { type: string, enum: [Downloaded, Evicted, Verified, Failed] }
|
||||
project_id: { type: string, format: uuid }
|
||||
aep_file_md5: { type: string }
|
||||
file_size_bytes: { type: integer, format: int64 }
|
||||
cache_used_gb: { type: integer }
|
||||
error_message: { type: string }
|
||||
responses:
|
||||
'204': { description: Recorded }
|
||||
|
||||
|
||||
components:
|
||||
securitySchemes:
|
||||
BearerAuth: { type: http, scheme: bearer, bearerFormat: JWT }
|
||||
ServiceToken: { type: http, scheme: bearer }
|
||||
NodeHmac:
|
||||
type: apiKey
|
||||
in: header
|
||||
name: X-Node-Signature
|
||||
|
||||
schemas:
|
||||
PaginationMeta:
|
||||
type: object
|
||||
properties:
|
||||
page: { type: integer }
|
||||
page_size: { type: integer }
|
||||
total: { type: integer }
|
||||
has_more: { type: boolean }
|
||||
|
||||
RenderJob:
|
||||
type: object
|
||||
properties:
|
||||
id: { type: string, format: uuid }
|
||||
saved_project_id: { type: string, format: uuid }
|
||||
name: { type: string }
|
||||
step: { type: string }
|
||||
render_progress: { type: integer }
|
||||
priority_queue: { type: string }
|
||||
price_type: { type: string }
|
||||
paid_price_minor: { type: integer, format: int64 }
|
||||
quality: { type: string }
|
||||
resolution: { type: string }
|
||||
frame_rate: { type: integer }
|
||||
duration_sec: { type: number }
|
||||
has_voiceover: { type: boolean }
|
||||
image_preview_b64: { type: string, nullable: true }
|
||||
failed_message: { type: string, nullable: true }
|
||||
export_id: { type: string, format: uuid, nullable: true }
|
||||
queued_at: { type: string, format: date-time }
|
||||
started_at: { type: string, format: date-time, nullable: true }
|
||||
completed_at: { type: string, format: date-time, nullable: true }
|
||||
|
||||
RenderJobCreate:
|
||||
type: object
|
||||
required: [saved_project_id, quality, resolution]
|
||||
properties:
|
||||
saved_project_id: { type: string, format: uuid }
|
||||
quality: { type: string, enum: [Low, Medium, High, Full, Lossless] }
|
||||
resolution: { type: string, enum: [HD, FullHD, TwoK, FourK] }
|
||||
frame_rate: { type: integer, enum: [24, 25, 30, 60] }
|
||||
is_60_fps: { type: boolean }
|
||||
price_type:
|
||||
type: string
|
||||
enum: [Free, Preview, Cash, Plan, Snapshot, Reseller]
|
||||
discount_code: { type: string }
|
||||
support_flatrender: { type: boolean }
|
||||
tell_me_when_done: { type: boolean }
|
||||
preferred_region: { type: string }
|
||||
|
||||
RenderJobDetail:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/RenderJob'
|
||||
- type: object
|
||||
properties:
|
||||
frame_jobs:
|
||||
type: array
|
||||
items: { $ref: '#/components/schemas/FrameJob' }
|
||||
retry_count: { type: integer }
|
||||
repair_attempts: { type: integer }
|
||||
export: { $ref: '#/components/schemas/Export', nullable: true }
|
||||
|
||||
FrameJob:
|
||||
type: object
|
||||
properties:
|
||||
id: { type: string, format: uuid }
|
||||
node_id: { type: string, format: uuid }
|
||||
start_frame: { type: integer }
|
||||
end_frame: { type: integer }
|
||||
order_value: { type: integer }
|
||||
status: { type: string }
|
||||
frames_rendered: { type: integer }
|
||||
frames_validated: { type: integer }
|
||||
attempt: { type: integer }
|
||||
last_error: { type: string, nullable: true }
|
||||
|
||||
RenderProgress:
|
||||
type: object
|
||||
properties:
|
||||
job_id: { type: string, format: uuid }
|
||||
step: { type: string }
|
||||
progress: { type: integer }
|
||||
current_frame: { type: integer, nullable: true }
|
||||
total_frames: { type: integer, nullable: true }
|
||||
eta_seconds: { type: integer, nullable: true }
|
||||
preview_b64: { type: string, nullable: true }
|
||||
active_nodes: { type: integer }
|
||||
message: { type: string, nullable: true }
|
||||
|
||||
Snapshot:
|
||||
type: object
|
||||
properties:
|
||||
id: { type: string, format: uuid }
|
||||
saved_project_id: { type: string, format: uuid }
|
||||
scene_key: { type: string }
|
||||
frame_number: { type: integer }
|
||||
status: { type: string }
|
||||
image_url: { type: string, nullable: true }
|
||||
thumbnail_url: { type: string, nullable: true }
|
||||
width: { type: integer, nullable: true }
|
||||
height: { type: integer, nullable: true }
|
||||
duration_ms: { type: integer, nullable: true }
|
||||
expires_at: { type: string, format: date-time }
|
||||
requested_at: { type: string, format: date-time }
|
||||
completed_at: { type: string, format: date-time, nullable: true }
|
||||
|
||||
Export:
|
||||
type: object
|
||||
properties:
|
||||
id: { type: string, format: uuid }
|
||||
saved_project_id: { type: string, format: uuid }
|
||||
path: { type: string }
|
||||
image: { type: string }
|
||||
size_bytes: { type: integer, format: int64 }
|
||||
duration_sec: { type: number }
|
||||
width: { type: integer }
|
||||
height: { type: integer }
|
||||
file_extension: { type: string }
|
||||
file_type: { type: string }
|
||||
render_quality: { type: string }
|
||||
create_type: { type: string }
|
||||
produce_date: { type: string, format: date-time }
|
||||
auto_delete_date: { type: string, format: date-time }
|
||||
|
||||
ExportDetail:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/Export'
|
||||
- type: object
|
||||
properties:
|
||||
files:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
id: { type: string, format: uuid }
|
||||
thumbnail: { type: string }
|
||||
path: { type: string }
|
||||
size_bytes: { type: integer, format: int64 }
|
||||
file_type: { type: string }
|
||||
|
||||
RenderNode:
|
||||
type: object
|
||||
properties:
|
||||
id: { type: string, format: uuid }
|
||||
name: { type: string }
|
||||
region: { type: string }
|
||||
node_ip: { type: string }
|
||||
worker_port: { type: integer }
|
||||
status: { type: string }
|
||||
node_kind: { type: string }
|
||||
owner_user_id: { type: string, format: uuid, nullable: true }
|
||||
current_ae_version: { type: string }
|
||||
node_agent_version: { type: string }
|
||||
ram_gb: { type: integer }
|
||||
cpu_cores: { type: integer }
|
||||
priority: { type: integer }
|
||||
is_active: { type: boolean }
|
||||
accepts_new_jobs: { type: boolean }
|
||||
last_heartbeat_at: { type: string, format: date-time }
|
||||
current_job_id: { type: string, format: uuid, nullable: true }
|
||||
last_cpu_pct: { type: integer }
|
||||
last_ram_available_mb: { type: integer }
|
||||
ae_running: { type: boolean }
|
||||
cache_used_gb: { type: integer }
|
||||
cached_template_count: { type: integer }
|
||||
lifetime_task_count: { type: integer, format: int64 }
|
||||
lifetime_crash_count: { type: integer }
|
||||
consecutive_failures: { type: integer }
|
||||
|
||||
RenderNodeCreate:
|
||||
type: object
|
||||
required: [name, region, node_ip, worker_port, current_ae_version]
|
||||
properties:
|
||||
name: { type: string }
|
||||
region: { type: string }
|
||||
node_ip: { type: string }
|
||||
worker_port: { type: integer }
|
||||
current_ae_version: { type: string }
|
||||
ram_gb: { type: integer }
|
||||
cpu_cores: { type: integer }
|
||||
node_kind: { type: string }
|
||||
owner_user_id: { type: string, format: uuid }
|
||||
priority: { type: integer }
|
||||
|
||||
RenderNodeDetail:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/RenderNode'
|
||||
- type: object
|
||||
properties:
|
||||
available_ae_versions: { type: array, items: { type: string } }
|
||||
cached_template_md5s: { type: array, items: { type: string } }
|
||||
last_maintenance_at: { type: string, format: date-time }
|
||||
next_maintenance_at: { type: string, format: date-time, nullable: true }
|
||||
|
||||
NodeHealth:
|
||||
type: object
|
||||
properties:
|
||||
node_id: { type: string, format: uuid }
|
||||
recorded_at: { type: string, format: date-time }
|
||||
status: { type: string }
|
||||
cpu_pct: { type: integer }
|
||||
ram_available_mb: { type: integer }
|
||||
ae_running: { type: boolean }
|
||||
current_job_id: { type: string, format: uuid, nullable: true }
|
||||
current_frame: { type: integer, nullable: true }
|
||||
cache_used_gb: { type: integer }
|
||||
|
||||
NodeHeartbeat:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/NodeHealth'
|
||||
|
||||
NodeCrash:
|
||||
type: object
|
||||
properties:
|
||||
id: { type: string, format: uuid }
|
||||
node_id: { type: string, format: uuid }
|
||||
render_job_id: { type: string, format: uuid, nullable: true }
|
||||
crashed_at: { type: string, format: date-time }
|
||||
last_known_frame: { type: integer, nullable: true }
|
||||
crash_signal: { type: string }
|
||||
error_log: { type: string }
|
||||
log_file_url: { type: string }
|
||||
auto_recovered: { type: boolean }
|
||||
recovery_action: { type: string }
|
||||
recovered_at: { type: string, format: date-time, nullable: true }
|
||||
|
||||
NodeUpdate:
|
||||
type: object
|
||||
properties:
|
||||
id: { type: string, format: uuid }
|
||||
update_file_name: { type: string }
|
||||
update_number: { type: integer }
|
||||
description: { type: string }
|
||||
target_ae_version: { type: string }
|
||||
in_update_queue: { type: boolean }
|
||||
rolled_out_to_node_ids: { type: array, items: { type: string, format: uuid } }
|
||||
create_date: { type: string, format: date-time }
|
||||
Reference in New Issue
Block a user