import type { IncomingMessage, ServerResponse } from 'node:http'; import { IMAGE_WORKSPACE_UNAVAILABLE_CODE, IMAGE_WORKSPACE_UNAVAILABLE_MESSAGE, type ImageWorkspaceGenerationSettings, type ImageWorkspaceReferenceUploadInput, type ImageWorkspaceSendMessageInput, type ImageWorkspaceSnapshot, } from '../../../shared/image-workspace'; import { LocalImageWorkspaceError } from '../../image-workspace/local-workspace'; import type { HostApiContext } from '../context'; import { parseJsonBody, sendJson } from '../route-utils'; const IMAGE_WORKSPACE_ROUTE_PREFIX = '/api/works/image-workspace'; function sendWorkspace(res: ServerResponse, workspace: ImageWorkspaceSnapshot): void { sendJson(res, 200, { success: true, status: 200, workspace }); } function decodedSegments(pathname: string): string[] | null { const suffix = pathname.slice(IMAGE_WORKSPACE_ROUTE_PREFIX.length).replace(/^\/+/, ''); if (!suffix) return []; try { return suffix.split('/').map((segment) => decodeURIComponent(segment)); } catch { return null; } } function asString(value: unknown): string { return typeof value === 'string' ? value : ''; } function asStringArray(value: unknown): string[] { return Array.isArray(value) && value.every((item) => typeof item === 'string') ? value : []; } function asSettings(value: unknown): ImageWorkspaceGenerationSettings { if (typeof value !== 'object' || value === null || Array.isArray(value)) return {}; const source = value as Record; return { modeId: asString(source.modeId) || undefined, modelId: asString(source.modelId) || undefined, aspectRatioId: asString(source.aspectRatioId) || undefined, resolutionId: asString(source.resolutionId) || undefined, outputCountId: asString(source.outputCountId) || undefined, }; } function sendRouteError(res: ServerResponse, error: unknown): void { if (error instanceof LocalImageWorkspaceError) { sendJson(res, error.status, { success: false, status: error.status, code: error.code, error: error.message, }); return; } if (error instanceof SyntaxError) { sendJson(res, 400, { success: false, status: 400, code: 'IMAGE_WORKSPACE_INVALID_JSON', error: '请求内容不是有效 JSON', }); return; } sendJson(res, 500, { success: false, status: 500, code: 'IMAGE_WORKSPACE_LOCAL_REQUEST_FAILED', error: error instanceof Error ? error.message : '创作空间请求失败', }); } export async function handleImageWorkspaceRoutes( req: IncomingMessage, res: ServerResponse, url: URL, ctx: HostApiContext, ): Promise { if (url.pathname !== IMAGE_WORKSPACE_ROUTE_PREFIX && !url.pathname.startsWith(`${IMAGE_WORKSPACE_ROUTE_PREFIX}/`)) { return false; } if (!ctx.imageWorkspace) { sendJson(res, 501, { success: false, status: 501, code: IMAGE_WORKSPACE_UNAVAILABLE_CODE, error: IMAGE_WORKSPACE_UNAVAILABLE_MESSAGE, }); return true; } const segments = decodedSegments(url.pathname); if (!segments) { sendJson(res, 400, { success: false, status: 400, error: '无效的绘画空间路径' }); return true; } try { if (segments.length === 0 && req.method === 'GET') { sendWorkspace(res, await ctx.imageWorkspace.getSnapshot()); return true; } if (segments.length === 1 && segments[0] === 'projects' && req.method === 'POST') { const body = await parseJsonBody<{ name?: unknown }>(req); sendWorkspace(res, await ctx.imageWorkspace.createProject(asString(body.name))); return true; } if (segments.length === 1 && segments[0] === 'local-data' && req.method === 'DELETE') { sendWorkspace(res, await ctx.imageWorkspace.reset()); return true; } if (segments.length === 3 && segments[0] === 'projects' && segments[2] === 'agents' && req.method === 'POST') { sendWorkspace(res, await ctx.imageWorkspace.addAgent(segments[1])); return true; } if (segments.length === 3 && segments[0] === 'projects' && segments[2] === 'messages' && req.method === 'POST') { const body = await parseJsonBody>(req); const input: ImageWorkspaceSendMessageInput = { projectId: segments[1], agentId: asString(body.agentId), prompt: asString(body.prompt), referenceImageIds: asStringArray(body.referenceImageIds), settings: asSettings(body.settings), }; sendWorkspace(res, await ctx.imageWorkspace.sendMessage(input)); return true; } if (segments.length === 3 && segments[0] === 'projects' && segments[2] === 'references' && req.method === 'POST') { const body = await parseJsonBody>(req); const input: ImageWorkspaceReferenceUploadInput = { projectId: segments[1], fileName: asString(body.fileName), mimeType: asString(body.mimeType), contentBase64: asString(body.contentBase64), }; const result = await ctx.imageWorkspace.uploadReference(input); sendJson(res, 200, { success: true, status: 200, ...result }); return true; } sendJson(res, 404, { success: false, status: 404, code: 'IMAGE_WORKSPACE_ROUTE_NOT_FOUND', error: `没有对应的绘画空间接口:${req.method ?? 'GET'} ${url.pathname}`, }); } catch (error) { sendRouteError(res, error); } return true; }