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,305 @@
|
||||
# =====================================================================
|
||||
# Common OpenAPI Components — referenced by every service spec via $ref
|
||||
# =====================================================================
|
||||
openapi: 3.0.3
|
||||
info:
|
||||
title: FlatRender Common Types
|
||||
version: "1.0"
|
||||
|
||||
components:
|
||||
# -------------------------------------------------------------------
|
||||
# Errors
|
||||
# -------------------------------------------------------------------
|
||||
schemas:
|
||||
Error:
|
||||
type: object
|
||||
required: [error]
|
||||
properties:
|
||||
error:
|
||||
type: object
|
||||
required: [code, message]
|
||||
properties:
|
||||
code:
|
||||
type: string
|
||||
example: NOT_FOUND
|
||||
message:
|
||||
type: string
|
||||
example: "Render job not found"
|
||||
details:
|
||||
type: object
|
||||
additionalProperties: true
|
||||
field_errors:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
field: { type: string }
|
||||
message: { type: string }
|
||||
trace_id:
|
||||
type: string
|
||||
format: uuid
|
||||
timestamp:
|
||||
type: string
|
||||
format: date-time
|
||||
|
||||
# ---------------------------------------------------------------
|
||||
# Pagination
|
||||
# ---------------------------------------------------------------
|
||||
PaginationMeta:
|
||||
type: object
|
||||
required: [page, page_size, total, has_more]
|
||||
properties:
|
||||
page: { type: integer, minimum: 1, example: 1 }
|
||||
page_size: { type: integer, minimum: 1, maximum: 200, example: 20 }
|
||||
total: { type: integer, minimum: 0, example: 137 }
|
||||
has_more: { type: boolean }
|
||||
next_cursor:
|
||||
type: string
|
||||
nullable: true
|
||||
description: Optional cursor for keyset pagination
|
||||
|
||||
PaginatedEnvelope:
|
||||
type: object
|
||||
required: [data, meta]
|
||||
properties:
|
||||
data:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
meta:
|
||||
$ref: '#/components/schemas/PaginationMeta'
|
||||
|
||||
# ---------------------------------------------------------------
|
||||
# Money
|
||||
# ---------------------------------------------------------------
|
||||
Money:
|
||||
type: object
|
||||
required: [amount_minor, currency]
|
||||
properties:
|
||||
amount_minor:
|
||||
type: integer
|
||||
format: int64
|
||||
description: Amount in minor units (rial, cent, etc.)
|
||||
example: 1500000
|
||||
currency:
|
||||
type: string
|
||||
enum: [IRR, USD, EUR]
|
||||
example: IRR
|
||||
|
||||
# ---------------------------------------------------------------
|
||||
# Audit
|
||||
# ---------------------------------------------------------------
|
||||
Timestamps:
|
||||
type: object
|
||||
properties:
|
||||
created_at: { type: string, format: date-time }
|
||||
updated_at: { type: string, format: date-time }
|
||||
deleted_at: { type: string, format: date-time, nullable: true }
|
||||
|
||||
# ---------------------------------------------------------------
|
||||
# Localized text (Persian + English)
|
||||
# ---------------------------------------------------------------
|
||||
LocalizedString:
|
||||
type: object
|
||||
additionalProperties:
|
||||
type: string
|
||||
example:
|
||||
fa: "عنوان"
|
||||
en: "Title"
|
||||
|
||||
# ---------------------------------------------------------------
|
||||
# Tenant
|
||||
# ---------------------------------------------------------------
|
||||
TenantContext:
|
||||
type: object
|
||||
required: [tenant_id, slug]
|
||||
properties:
|
||||
tenant_id: { type: string, format: uuid }
|
||||
slug: { type: string }
|
||||
kind:
|
||||
type: string
|
||||
enum: [Internal, Reseller, Enterprise]
|
||||
|
||||
# ---------------------------------------------------------------
|
||||
# User reference (lightweight)
|
||||
# ---------------------------------------------------------------
|
||||
UserRef:
|
||||
type: object
|
||||
required: [id]
|
||||
properties:
|
||||
id: { type: string, format: uuid }
|
||||
full_name: { type: string, nullable: true }
|
||||
avatar_url: { type: string, nullable: true }
|
||||
|
||||
# ---------------------------------------------------------------
|
||||
# File reference
|
||||
# ---------------------------------------------------------------
|
||||
FileRef:
|
||||
type: object
|
||||
required: [id, url, file_type]
|
||||
properties:
|
||||
id: { type: string, format: uuid }
|
||||
url: { type: string }
|
||||
thumbnail_url: { type: string, nullable: true }
|
||||
file_type:
|
||||
type: string
|
||||
enum: [Video, Image, Audio, Voiceover, Document, Other]
|
||||
mime_type: { type: string, nullable: true }
|
||||
size_bytes: { type: integer, format: int64 }
|
||||
duration_sec: { type: number, nullable: true }
|
||||
width: { type: integer, nullable: true }
|
||||
height: { type: integer, nullable: true }
|
||||
|
||||
# ---------------------------------------------------------------
|
||||
# Enums (reused everywhere)
|
||||
# ---------------------------------------------------------------
|
||||
ChooseMode:
|
||||
type: string
|
||||
enum: [FIX, FLEXIBLE, MockUp, MusicVisualizer, VoiceOver]
|
||||
|
||||
Resolution:
|
||||
type: string
|
||||
enum: [HD, FullHD, TwoK, FourK]
|
||||
|
||||
SceneType:
|
||||
type: string
|
||||
enum: [Normal, Config, DesignStart, DesignEnd]
|
||||
|
||||
JustifyKind:
|
||||
type: string
|
||||
enum: [LEFT_JUSTIFY, CENTER_JUSTIFY, RIGHT_JUSTIFY, FULL_JUSTIFY]
|
||||
|
||||
RenderStep:
|
||||
type: string
|
||||
enum:
|
||||
- Queued
|
||||
- Preparing
|
||||
- TemplateCache
|
||||
- JsxGen
|
||||
- Music
|
||||
- Rendering
|
||||
- Validating
|
||||
- Repairing
|
||||
- Optimisation
|
||||
- Video
|
||||
- Mixing
|
||||
- Final
|
||||
- Uploading
|
||||
- Done
|
||||
- Failed
|
||||
- Cancelled
|
||||
|
||||
RenderPriorityQueue:
|
||||
type: string
|
||||
enum: [snapshot, vip, paid, preview, mockup, voiceover]
|
||||
|
||||
PriceType:
|
||||
type: string
|
||||
enum: [Free, Preview, Cash, Plan, Snapshot, Reseller]
|
||||
|
||||
RenderQuality:
|
||||
type: string
|
||||
enum: [Low, Medium, High, Full, Lossless]
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# Common headers
|
||||
# -------------------------------------------------------------------
|
||||
parameters:
|
||||
TenantIdHeader:
|
||||
name: X-Tenant-Id
|
||||
in: header
|
||||
required: true
|
||||
schema: { type: string, format: uuid }
|
||||
description: Tenant context (carried by JWT on public APIs)
|
||||
|
||||
RequestIdHeader:
|
||||
name: X-Request-Id
|
||||
in: header
|
||||
required: false
|
||||
schema: { type: string, format: uuid }
|
||||
description: Optional client-supplied correlation ID
|
||||
|
||||
PageParam:
|
||||
name: page
|
||||
in: query
|
||||
schema: { type: integer, minimum: 1, default: 1 }
|
||||
|
||||
PageSizeParam:
|
||||
name: page_size
|
||||
in: query
|
||||
schema: { type: integer, minimum: 1, maximum: 200, default: 20 }
|
||||
|
||||
CursorParam:
|
||||
name: cursor
|
||||
in: query
|
||||
schema: { type: string }
|
||||
description: Keyset pagination cursor
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# Standard responses
|
||||
# -------------------------------------------------------------------
|
||||
responses:
|
||||
BadRequest:
|
||||
description: Validation error or malformed request
|
||||
content:
|
||||
application/json:
|
||||
schema: { $ref: '#/components/schemas/Error' }
|
||||
Unauthorized:
|
||||
description: Missing or invalid auth
|
||||
content:
|
||||
application/json:
|
||||
schema: { $ref: '#/components/schemas/Error' }
|
||||
Forbidden:
|
||||
description: Authenticated but not allowed
|
||||
content:
|
||||
application/json:
|
||||
schema: { $ref: '#/components/schemas/Error' }
|
||||
NotFound:
|
||||
description: Resource not found
|
||||
content:
|
||||
application/json:
|
||||
schema: { $ref: '#/components/schemas/Error' }
|
||||
Conflict:
|
||||
description: Resource conflict
|
||||
content:
|
||||
application/json:
|
||||
schema: { $ref: '#/components/schemas/Error' }
|
||||
TooManyRequests:
|
||||
description: Rate limited
|
||||
content:
|
||||
application/json:
|
||||
schema: { $ref: '#/components/schemas/Error' }
|
||||
InternalError:
|
||||
description: Unexpected server error
|
||||
content:
|
||||
application/json:
|
||||
schema: { $ref: '#/components/schemas/Error' }
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# Security schemes
|
||||
# -------------------------------------------------------------------
|
||||
securitySchemes:
|
||||
BearerAuth:
|
||||
type: http
|
||||
scheme: bearer
|
||||
bearerFormat: JWT
|
||||
description: JWT with claims sub, tenant_id, scope
|
||||
ApiKeyAuth:
|
||||
type: apiKey
|
||||
in: header
|
||||
name: X-API-Key
|
||||
description: B2B reseller API key (fr_live_..., fr_test_...)
|
||||
ApiKeySignature:
|
||||
type: apiKey
|
||||
in: header
|
||||
name: X-API-Signature
|
||||
description: HMAC-SHA256 of (timestamp + body) using key secret
|
||||
ServiceToken:
|
||||
type: http
|
||||
scheme: bearer
|
||||
description: Inter-service short-lived JWT issued by control plane
|
||||
NodeHmac:
|
||||
type: apiKey
|
||||
in: header
|
||||
name: X-Node-Signature
|
||||
description: HMAC for orchestrator <-> node-agent comms
|
||||
Reference in New Issue
Block a user