feat: complete remaining Go backend modules
This commit is contained in:
316
backend/internal/httpapi/openapi.go
Normal file
316
backend/internal/httpapi/openapi.go
Normal file
@@ -0,0 +1,316 @@
|
||||
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", "bailian", "mock"}},
|
||||
"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"),
|
||||
"prompt": map[string]any{"type": "string", "description": "Prompt text. Required for image.generate unless promptAssembly is supplied."},
|
||||
"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": "Video settings for video.generate.",
|
||||
"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": 15},
|
||||
"resolution": map[string]any{"type": "string", "enum": []string{"480p", "720p", "1080p"}},
|
||||
},
|
||||
},
|
||||
"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 品牌短视频",
|
||||
"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"}}
|
||||
}
|
||||
Reference in New Issue
Block a user