357 lines
13 KiB
TypeScript
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" });
|
|
}
|