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:
soroush.asadi
2026-05-29 23:29:31 +03:30
parent 53ea78a00d
commit 90ac0b81d1
7636 changed files with 3707504 additions and 240 deletions
+305
View File
@@ -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