Files
NianAIGC/app/api/v1/openapi.json/route.ts
2026-05-29 14:32:02 +08:00

357 lines
13 KiB
TypeScript

import { jsonOk } from "@/lib/server/api";
import { requestOrigin } from "@/lib/server/runtime";
export const runtime = "nodejs";
export async function GET(request: Request) {
return jsonOk({
openapi: "3.1.0",
info: {
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: [
{ url: requestOrigin(request), description: "Current deployment" }
],
security: [{ bearerApiKey: [] }, { headerApiKey: [] }],
components: {
securitySchemes: {
bearerApiKey: { type: "http", scheme: "bearer" },
headerApiKey: { type: "apiKey", in: "header", name: "X-Zhinian-Api-Key" }
},
schemas: {
ErrorResponse: {
type: "object",
required: ["error"],
properties: {
error: { type: "string" }
}
},
Asset: {
type: "object",
required: ["id", "kind", "name", "url", "source", "createdAt"],
properties: {
id: { type: "string", example: "asset_mpqe9g85_12635f8cd8" },
ownerId: { type: "string" },
kind: { type: "string", enum: ["image", "video", "mask", "reference", "other"] },
name: { type: "string", example: "result.png" },
url: { type: "string", format: "uri" },
storagePath: { type: "string" },
source: { type: "string", enum: ["upload", "generated", "edited", "upscaled", "external", "seed"] },
tags: { type: "array", items: { type: "string" } },
metadata: { type: "object", additionalProperties: true },
createdAt: { type: "string", format: "date-time" },
updatedAt: { type: "string", format: "date-time" }
}
},
GenerationJob: {
type: "object",
required: ["id", "capability", "provider", "status", "createdAt", "updatedAt"],
properties: {
id: { type: "string", example: "job_mpqe3wtt_12ed738079" },
externalClientId: { type: "string" },
capability: { $ref: "#/components/schemas/GenerationCapability" },
provider: { type: "string", enum: ["volcengine-visual", "evolink", "seedance", "mock"] },
reqKey: { type: "string" },
status: { $ref: "#/components/schemas/GenerationStatus" },
prompt: { type: "string" },
inputAssetIds: { type: "array", items: { type: "string" } },
inputUrls: { type: "array", items: { type: "string", format: "uri" } },
outputAssetIds: { type: "array", items: { type: "string" } },
providerTaskId: { type: "string" },
error: {
type: "object",
properties: {
code: { oneOf: [{ type: "string" }, { type: "number" }] },
message: { type: "string" },
retryable: { type: "boolean" }
}
},
idempotencyKey: { type: "string" },
priority: { type: "integer" },
attempts: { type: "integer" },
scheduledAt: { type: "string", format: "date-time" },
completedAt: { type: "string", format: "date-time" },
webhookUrl: { type: "string", format: "uri" },
webhookAttempts: { type: "integer" },
webhookLastStatus: { type: "object", additionalProperties: true },
createdAt: { type: "string", format: "date-time" },
updatedAt: { type: "string", format: "date-time" }
}
},
GenerationCapability: {
type: "string",
enum: ["image.generate", "image.inpaint", "image.upscale", "video.generate"]
},
GenerationStatus: {
type: "string",
enum: ["queued", "running", "succeeded", "failed", "expired", "cancelled"]
},
PromptMaterial: {
type: "object",
properties: {
id: { type: "string" },
url: { type: "string", format: "uri" },
type: { type: "string", enum: ["image", "video", "audio"] },
role: { type: "string" },
label: { type: "string" },
name: { type: "string" }
}
},
CreateJobRequest: {
type: "object",
required: ["capability"],
properties: {
capability: { $ref: "#/components/schemas/GenerationCapability" },
prompt: { type: "string", description: "Prompt text. Required for image.generate unless promptAssembly is supplied." },
inputUrls: { type: "array", items: { type: "string", format: "uri" }, description: "Reference image URLs for image capabilities." },
imageUrls: { type: "array", items: { type: "string", format: "uri" }, description: "Alias for image input URLs." },
inputAssetIds: { type: "array", items: { type: "string" } },
materials: { type: "array", items: { $ref: "#/components/schemas/PromptMaterial" } },
settings: {
type: "object",
description: "Video settings for video.generate.",
properties: {
ratio: { type: "string", enum: ["16:9", "4:3", "1:1", "3:4", "9:16", "21:9", "adaptive"] },
duration: { type: "integer", minimum: 4, maximum: 15 },
resolution: { type: "string", enum: ["480p", "720p", "1080p"] }
}
},
width: { type: "integer", example: 1440 },
height: { type: "integer", example: 2560 },
scale: { type: "number", minimum: 1, maximum: 100, description: "Jimeng text influence for image.generate and detail strength for image.upscale." },
force_single: { type: "boolean" },
quality: { type: "string", enum: ["low", "medium", "high"], description: "EvoLink image quality for image.generate and image.inpaint." },
resolution: { type: "string", enum: ["4k", "8k"], description: "Upscale resolution for image.upscale." },
seed: { type: "integer" },
priority: { type: "integer", minimum: -100, maximum: 100 },
webhookUrl: { type: "string", format: "uri" },
idempotencyKey: { type: "string", description: "Optional body-level idempotency key. Header Idempotency-Key is preferred." }
}
},
RegisterAssetRequest: {
type: "object",
required: ["url"],
properties: {
url: { type: "string", format: "uri" },
name: { type: "string" },
kind: { type: "string", enum: ["image", "video", "mask", "reference", "other"] },
tags: { type: "array", items: { type: "string" } }
}
},
WebhookPayload: {
type: "object",
required: ["event", "job"],
properties: {
event: { type: "string", example: "generation.succeeded" },
job: { $ref: "#/components/schemas/GenerationJob" }
}
}
}
},
paths: {
"/api/v1/capabilities": {
get: {
summary: "List generation capabilities",
responses: {
"200": jsonResponse("Capabilities and active providers")
}
}
},
"/api/v1/assets": {
get: {
summary: "List assets visible to the authenticated API client",
responses: {
"200": jsonResponse("Assets", {
type: "object",
properties: {
assets: { type: "array", items: { $ref: "#/components/schemas/Asset" } }
}
})
}
},
post: {
summary: "Upload files or register an external asset URL",
requestBody: {
required: true,
content: {
"application/json": { schema: { $ref: "#/components/schemas/RegisterAssetRequest" } },
"multipart/form-data": {
schema: {
type: "object",
properties: {
files: { type: "array", items: { type: "string", format: "binary" } }
}
}
}
}
},
responses: {
"201": jsonResponse("Created asset"),
"400": errorResponse(),
"401": errorResponse()
}
}
},
"/api/v1/assets/{id}": {
get: {
summary: "Get one asset visible to the authenticated API client",
parameters: [pathId()],
responses: {
"200": jsonResponse("Asset", {
type: "object",
properties: {
asset: { $ref: "#/components/schemas/Asset" }
}
}),
"404": errorResponse()
}
}
},
"/api/v1/assets/{id}/download": {
get: {
summary: "Download an output or uploaded asset",
parameters: [pathId()],
responses: {
"200": {
description: "Binary file",
headers: {
"Content-Disposition": { schema: { type: "string" } },
"Content-Length": { schema: { type: "string" } }
},
content: {
"application/octet-stream": { schema: { type: "string", format: "binary" } },
"image/png": { schema: { type: "string", format: "binary" } },
"image/jpeg": { schema: { type: "string", format: "binary" } },
"video/mp4": { schema: { type: "string", format: "binary" } }
}
},
"404": errorResponse()
}
}
},
"/api/v1/jobs": {
get: {
summary: "List jobs for the authenticated API client",
parameters: [
queryParam("status", { $ref: "#/components/schemas/GenerationStatus" }),
queryParam("capability", { $ref: "#/components/schemas/GenerationCapability" }),
queryParam("limit", { type: "integer", minimum: 1, maximum: 200 }),
queryParam("before", { type: "string", format: "date-time" })
],
responses: {
"200": jsonResponse("Jobs", {
type: "object",
properties: {
jobs: { type: "array", items: { $ref: "#/components/schemas/GenerationJob" } }
}
})
}
},
post: {
summary: "Create a queued generation job",
parameters: [
{
name: "Idempotency-Key",
in: "header",
required: false,
schema: { type: "string" },
description: "Reuse the same key for safe retries with the same request body."
}
],
requestBody: {
required: true,
content: {
"application/json": {
schema: { $ref: "#/components/schemas/CreateJobRequest" },
examples: {
imageGenerate: {
summary: "Image generation",
value: {
capability: "image.generate",
prompt: "生成一张专业产品主图",
width: 1440,
height: 2560,
webhookUrl: "https://example.com/zhinian/webhook"
}
},
videoGenerate: {
summary: "Video generation",
value: {
capability: "video.generate",
prompt: "生成一条 9:16 品牌短视频",
settings: { ratio: "9:16", duration: 5, resolution: "720p" }
}
}
}
}
}
},
responses: {
"202": jsonResponse("Queued job", {
type: "object",
properties: {
job: { $ref: "#/components/schemas/GenerationJob" },
reused: { type: "boolean" }
}
}),
"409": errorResponse()
}
}
},
"/api/v1/jobs/{id}": {
get: {
summary: "Get one job",
parameters: [pathId()],
responses: {
"200": jsonResponse("Job", {
type: "object",
properties: {
job: { $ref: "#/components/schemas/GenerationJob" }
}
}),
"404": errorResponse()
}
}
},
"/api/v1/jobs/{id}/cancel": {
post: {
summary: "Cancel a queued or running job",
parameters: [pathId()],
responses: {
"200": jsonResponse("Cancelled job", {
type: "object",
properties: {
job: { $ref: "#/components/schemas/GenerationJob" }
}
}),
"404": errorResponse()
}
}
}
}
});
}
function pathId() {
return { name: "id", in: "path", required: true, schema: { type: "string" } };
}
function queryParam(name: string, schema: Record<string, unknown>) {
return { name, in: "query", required: false, schema };
}
function jsonResponse(description: string, schema: Record<string, unknown> = { type: "object", additionalProperties: true }) {
return {
description,
content: {
"application/json": { schema }
}
};
}
function errorResponse() {
return jsonResponse("Error", { $ref: "#/components/schemas/ErrorResponse" });
}