Files
NianAIGC/backend/internal/httpapi/openapi.go
2026-08-25 14:06:04 +08:00

325 lines
16 KiB
Go

package httpapi
func openAPIDocument(origin string) map[string]any {
return map[string]any{
"openapi": "3.1.0",
"info": map[string]any{
"title": "智念AIGC平台 Public API",
"version": "1.0.0",
"description": "Public server-to-server API for uploading assets, creating image/video generation jobs, polling job status, downloading outputs, and receiving webhooks.",
},
"servers": []any{map[string]any{"url": origin, "description": "Current deployment"}},
"security": []any{
map[string]any{"bearerApiKey": []any{}},
map[string]any{"headerApiKey": []any{}},
},
"components": map[string]any{
"securitySchemes": map[string]any{
"bearerApiKey": map[string]any{"type": "http", "scheme": "bearer"},
"headerApiKey": map[string]any{"type": "apiKey", "in": "header", "name": "X-Zhinian-Api-Key"},
},
"schemas": openAPISchemas(),
},
"paths": openAPIPaths(),
}
}
func openAPISchemas() map[string]any {
return map[string]any{
"ErrorResponse": map[string]any{
"type": "object", "required": []string{"error"},
"properties": map[string]any{"error": map[string]any{"type": "string"}},
},
"Asset": map[string]any{
"type": "object",
"required": []string{"id", "kind", "name", "url", "source", "createdAt"},
"properties": map[string]any{
"id": map[string]any{"type": "string", "example": "asset_mpqe9g85_12635f8cd8"},
"ownerId": map[string]any{"type": "string"},
"kind": map[string]any{"type": "string", "enum": []string{"image", "video", "mask", "reference", "other"}},
"name": map[string]any{"type": "string", "example": "result.png"},
"url": map[string]any{"type": "string", "format": "uri"},
"storagePath": map[string]any{"type": "string"},
"source": map[string]any{"type": "string", "enum": []string{"upload", "generated", "external", "seed"}},
"tags": map[string]any{"type": "array", "items": map[string]any{"type": "string"}},
"metadata": map[string]any{"type": "object", "additionalProperties": true},
"createdAt": map[string]any{"type": "string", "format": "date-time"},
"updatedAt": map[string]any{"type": "string", "format": "date-time"},
},
},
"GenerationJob": map[string]any{
"type": "object",
"required": []string{"id", "capability", "provider", "status", "createdAt", "updatedAt"},
"properties": map[string]any{
"id": map[string]any{"type": "string", "example": "job_mpqe3wtt_12ed738079"},
"ownerId": map[string]any{"type": "string", "example": "api:partner-a"},
"externalClientId": map[string]any{"type": "string"},
"capability": openAPIRef("GenerationCapability"),
"provider": map[string]any{"type": "string", "enum": []string{"volcengine-visual", "evolink", "seedance", "seedream", "bailian"}},
"reqKey": map[string]any{"type": "string"},
"status": openAPIRef("GenerationStatus"),
"prompt": map[string]any{"type": "string"},
"inputAssetIds": openAPIStringArray(),
"inputUrls": map[string]any{"type": "array", "items": map[string]any{"type": "string", "format": "uri"}},
"outputAssetIds": openAPIStringArray(),
"providerTaskId": map[string]any{"type": "string"},
"error": openAPIJobError(),
"idempotencyKey": map[string]any{"type": "string"},
"priority": map[string]any{"type": "integer"},
"attempts": map[string]any{"type": "integer"},
"scheduledAt": map[string]any{"type": "string", "format": "date-time"},
"completedAt": map[string]any{"type": "string", "format": "date-time"},
"webhookUrl": map[string]any{"type": "string", "format": "uri"},
"webhookAttempts": map[string]any{"type": "integer"},
"webhookLastStatus": map[string]any{"type": "object", "additionalProperties": true},
"createdAt": map[string]any{"type": "string", "format": "date-time"},
"updatedAt": map[string]any{"type": "string", "format": "date-time"},
},
},
"GenerationCapability": map[string]any{"type": "string", "enum": []string{"image.generate", "video.generate"}},
"GenerationStatus": map[string]any{"type": "string", "enum": []string{"queued", "running", "succeeded", "failed", "expired", "cancelled"}},
"PromptMaterial": map[string]any{
"type": "object",
"properties": map[string]any{
"id": map[string]any{"type": "string"},
"url": map[string]any{"type": "string", "format": "uri"},
"type": map[string]any{"type": "string", "enum": []string{"image", "video", "audio"}},
"role": map[string]any{"type": "string"},
"label": map[string]any{"type": "string"},
"name": map[string]any{"type": "string"},
},
},
"CreateJobRequest": map[string]any{
"type": "object",
"required": []string{"capability"},
"properties": map[string]any{
"capability": openAPIRef("GenerationCapability"),
"engine": map[string]any{"type": "string", "enum": []string{"jimeng", "seedream", "evolink", "bailian", "seedance"}},
"model": map[string]any{"type": "string", "enum": []string{"doubao-seedance-2-0-260128", "doubao-seedance-2-5-260628"}, "description": "Optional allow-listed Seedance model for video.generate."},
"prompt": map[string]any{"type": "string", "description": "Prompt text. Required for generation except Seedream layer decomposition, where an empty prompt triggers automatic decomposition."},
"inputUrls": map[string]any{"type": "array", "items": map[string]any{"type": "string", "format": "uri"}, "description": "Reference image URLs for image capabilities."},
"imageUrls": map[string]any{"type": "array", "items": map[string]any{"type": "string", "format": "uri"}, "description": "Alias for image input URLs."},
"inputAssetIds": openAPIStringArray(),
"materials": map[string]any{"type": "array", "items": openAPIRef("PromptMaterial")},
"settings": map[string]any{
"type": "object", "description": "Provider-specific image or video settings.",
"properties": map[string]any{
"ratio": map[string]any{"type": "string", "enum": []string{"16:9", "4:3", "1:1", "3:4", "9:16", "21:9", "adaptive"}},
"duration": map[string]any{"type": "integer", "minimum": 4, "maximum": 30, "description": "Seedance 2.0 supports up to 15 seconds; Seedance 2.5 supports up to 30 seconds."},
"resolution": map[string]any{"type": "string", "enum": []string{"480p", "720p", "1080p"}},
"size": map[string]any{"type": "string", "enum": []string{"auto", "1K", "1.5K", "2K"}, "description": "Seedream 5.0 Pro output size. auto is available for layer decomposition only."},
"outputFormat": map[string]any{"type": "string", "enum": []string{"png", "jpeg"}},
"optimizeMode": map[string]any{"type": "string", "enum": []string{"standard", "fast"}},
"creationMode": map[string]any{"type": "string", "enum": []string{"basic", "interactive", "layers"}, "description": "Seedream 5.0 Pro image workflow."},
"layerDecomposition": map[string]any{"type": "boolean", "description": "Enables Seedream 5.0 Pro layer decomposition; exactly one image is required."},
},
},
"width": map[string]any{"type": "integer", "example": 1440},
"height": map[string]any{"type": "integer", "example": 2560},
"scale": map[string]any{"type": "number", "minimum": 1, "maximum": 100, "description": "Jimeng text influence for image.generate."},
"force_single": map[string]any{"type": "boolean"},
"quality": map[string]any{"type": "string", "enum": []string{"low", "medium", "high"}, "description": "EvoLink image quality for image.generate."},
"priority": map[string]any{"type": "integer", "minimum": -100, "maximum": 100},
"webhookUrl": map[string]any{"type": "string", "format": "uri"},
"idempotencyKey": map[string]any{"type": "string", "description": "Optional body-level idempotency key. Header Idempotency-Key is preferred."},
},
},
"RegisterAssetRequest": map[string]any{
"type": "object", "required": []string{"url"},
"properties": map[string]any{
"url": map[string]any{"type": "string", "format": "uri"},
"name": map[string]any{"type": "string"},
"kind": map[string]any{"type": "string", "enum": []string{"image", "video", "mask", "reference", "other"}},
"tags": openAPIStringArray(),
},
},
"WebhookPayload": map[string]any{
"type": "object",
"required": []string{"jobId", "status", "capability", "outputAssetIds", "updatedAt"},
"properties": map[string]any{
"jobId": map[string]any{"type": "string", "example": "job_mpqe3wtt_12ed738079"},
"status": openAPIRef("GenerationStatus"),
"capability": openAPIRef("GenerationCapability"),
"outputAssetIds": openAPIStringArray(),
"error": openAPIJobError(),
"updatedAt": map[string]any{"type": "string", "format": "date-time"},
},
},
}
}
func openAPIPaths() map[string]any {
return map[string]any{
"/api/v1/capabilities": map[string]any{
"get": map[string]any{
"summary": "List generation capabilities",
"responses": map[string]any{"200": openAPIJSONResponse("Capabilities and active providers", nil)},
},
},
"/api/v1/assets": map[string]any{
"get": map[string]any{
"summary": "List assets visible to the authenticated API client",
"responses": map[string]any{"200": openAPIJSONResponse("Assets", map[string]any{
"type": "object", "properties": map[string]any{"assets": map[string]any{"type": "array", "items": openAPIRef("Asset")}},
})},
},
"post": map[string]any{
"summary": "Upload files or register an external asset URL",
"requestBody": map[string]any{
"required": true,
"content": map[string]any{
"application/json": map[string]any{"schema": openAPIRef("RegisterAssetRequest")},
"multipart/form-data": map[string]any{"schema": map[string]any{
"type": "object", "properties": map[string]any{"files": map[string]any{"type": "array", "items": map[string]any{"type": "string", "format": "binary"}}},
}},
},
},
"responses": map[string]any{
"201": openAPIJSONResponse("Created asset", nil),
"400": openAPIErrorResponse(), "401": openAPIErrorResponse(),
},
},
},
"/api/v1/assets/{id}": map[string]any{
"get": map[string]any{
"summary": "Get one asset visible to the authenticated API client",
"parameters": []any{openAPIPathID()},
"responses": map[string]any{
"200": openAPIJSONResponse("Asset", map[string]any{"type": "object", "properties": map[string]any{"asset": openAPIRef("Asset")}}),
"404": openAPIErrorResponse(),
},
},
},
"/api/v1/assets/{id}/download": map[string]any{
"get": map[string]any{
"summary": "Download an output or uploaded asset",
"parameters": []any{openAPIPathID()},
"responses": map[string]any{
"200": map[string]any{
"description": "Binary file",
"headers": map[string]any{
"Content-Disposition": map[string]any{"schema": map[string]any{"type": "string"}},
"Content-Length": map[string]any{"schema": map[string]any{"type": "string"}},
},
"content": map[string]any{
"application/octet-stream": openAPIBinaryContent(),
"image/png": openAPIBinaryContent(),
"image/jpeg": openAPIBinaryContent(),
"video/mp4": openAPIBinaryContent(),
},
},
"404": openAPIErrorResponse(),
},
},
},
"/api/v1/jobs": map[string]any{
"get": map[string]any{
"summary": "List jobs for the authenticated API client",
"parameters": []any{
openAPIQueryParameter("status", openAPIRef("GenerationStatus")),
openAPIQueryParameter("capability", openAPIRef("GenerationCapability")),
openAPIQueryParameter("limit", map[string]any{"type": "integer", "minimum": 1, "maximum": 200}),
openAPIQueryParameter("before", map[string]any{"type": "string", "format": "date-time"}),
},
"responses": map[string]any{"200": openAPIJSONResponse("Jobs", map[string]any{
"type": "object", "properties": map[string]any{"jobs": map[string]any{"type": "array", "items": openAPIRef("GenerationJob")}},
})},
},
"post": map[string]any{
"summary": "Create a queued generation job",
"parameters": []any{map[string]any{
"name": "Idempotency-Key", "in": "header", "required": false,
"schema": map[string]any{"type": "string"},
"description": "Reuse the same key for safe retries with the same request body.",
}},
"requestBody": map[string]any{
"required": true,
"content": map[string]any{"application/json": map[string]any{
"schema": openAPIRef("CreateJobRequest"),
"examples": map[string]any{
"imageGenerate": map[string]any{"summary": "Image generation", "value": map[string]any{
"capability": "image.generate", "prompt": "生成一张专业产品主图", "width": 1440, "height": 2560,
"webhookUrl": "https://example.com/zhinian/webhook",
}},
"videoGenerate": map[string]any{"summary": "Video generation", "value": map[string]any{
"capability": "video.generate", "prompt": "生成一条 9:16 品牌短视频",
"model": "doubao-seedance-2-5-260628",
"settings": map[string]any{"ratio": "9:16", "duration": 5, "resolution": "720p"},
}},
},
}},
},
"responses": map[string]any{
"202": openAPIJSONResponse("Queued job", map[string]any{
"type": "object", "properties": map[string]any{"job": openAPIRef("GenerationJob"), "reused": map[string]any{"type": "boolean"}},
}),
"409": openAPIErrorResponse(),
},
},
},
"/api/v1/jobs/{id}": map[string]any{
"get": map[string]any{
"summary": "Get one job", "parameters": []any{openAPIPathID()},
"responses": map[string]any{
"200": openAPIJSONResponse("Job", map[string]any{"type": "object", "properties": map[string]any{"job": openAPIRef("GenerationJob")}}),
"404": openAPIErrorResponse(),
},
},
},
"/api/v1/jobs/{id}/cancel": map[string]any{
"post": map[string]any{
"summary": "Cancel a queued or running job", "parameters": []any{openAPIPathID()},
"responses": map[string]any{
"200": openAPIJSONResponse("Cancelled job", map[string]any{"type": "object", "properties": map[string]any{"job": openAPIRef("GenerationJob")}}),
"404": openAPIErrorResponse(),
},
},
},
}
}
func openAPIRef(name string) map[string]any {
return map[string]any{"$ref": "#/components/schemas/" + name}
}
func openAPIStringArray() map[string]any {
return map[string]any{"type": "array", "items": map[string]any{"type": "string"}}
}
func openAPIJobError() map[string]any {
return map[string]any{
"type": "object",
"properties": map[string]any{
"code": map[string]any{"oneOf": []any{map[string]any{"type": "string"}, map[string]any{"type": "number"}}},
"message": map[string]any{"type": "string"},
"retryable": map[string]any{"type": "boolean"},
},
}
}
func openAPIPathID() map[string]any {
return map[string]any{"name": "id", "in": "path", "required": true, "schema": map[string]any{"type": "string"}}
}
func openAPIQueryParameter(name string, schema map[string]any) map[string]any {
return map[string]any{"name": name, "in": "query", "required": false, "schema": schema}
}
func openAPIJSONResponse(description string, schema map[string]any) map[string]any {
if schema == nil {
schema = map[string]any{"type": "object", "additionalProperties": true}
}
return map[string]any{
"description": description,
"content": map[string]any{"application/json": map[string]any{"schema": schema}},
}
}
func openAPIErrorResponse() map[string]any {
return openAPIJSONResponse("Error", openAPIRef("ErrorResponse"))
}
func openAPIBinaryContent() map[string]any {
return map[string]any{"schema": map[string]any{"type": "string", "format": "binary"}}
}