import type { WebAssetItem } from "../types"; import { isRecord, serverRequest } from "./serverConnection"; export interface ServerAssetItem extends WebAssetItem { id: string; url?: string | null; ossKey?: string | null; tags: string[]; sourceTaskId?: string | null; sourceProjectId?: string | null; metadata?: Record; createdAt?: string; } export interface CreateAssetInput { type: WebAssetItem["type"] | "image" | "asset" | "other"; name: string; description?: string; url?: string; imageUrl?: string; ossKey?: string; tags?: string[]; status?: WebAssetItem["status"] | "pending" | "failed"; sourceTaskId?: string; sourceProjectId?: string; metadata?: Record; } export interface DeleteAssetOptions { cleanupUserData?: boolean; } function toStringValue(value: unknown, fallback = ""): string { if (typeof value === "string") return value.trim() || fallback; if (typeof value === "number" && Number.isFinite(value)) return String(value); return fallback; } function normalizeAssetType(value: unknown): WebAssetItem["type"] { const type = toStringValue(value); if ( type === "character" || type === "scene" || type === "prop" || type === "video" || type === "image" || type === "asset" || type === "other" ) { return type; } return "other"; } function normalizeAssetStatus(value: unknown): WebAssetItem["status"] { const status = toStringValue(value); if ( status === "ready" || status === "draft" || status === "reviewing" || status === "pending" || status === "failed" ) { return status; } return "ready"; } function normalizeTags(value: unknown): string[] { if (!Array.isArray(value)) return []; const tags: string[] = []; for (const item of value) { const tag = toStringValue(item); if (tag) tags.push(tag); } return tags; } function normalizeAsset(raw: unknown): ServerAssetItem { const item = isRecord(raw) ? raw : {}; const url = toStringValue(item.url ?? item.imageUrl) || null; return { id: toStringValue(item.id, `asset-${Date.now()}`), type: normalizeAssetType(item.type), name: toStringValue(item.name, "未命名素材"), description: toStringValue(item.description, "从服务器资产库同步的素材。"), imageUrl: url || "", url, ossKey: toStringValue(item.ossKey ?? item.oss_key) || null, tags: normalizeTags(item.tags), status: normalizeAssetStatus(item.status), sourceTaskId: toStringValue(item.sourceTaskId ?? item.source_task_id) || null, sourceProjectId: toStringValue(item.sourceProjectId ?? item.source_project_id) || null, metadata: isRecord(item.metadata) ? item.metadata : {}, createdAt: toStringValue(item.createdAt ?? item.created_at), updatedAt: toStringValue(item.updatedAt ?? item.updated_at, "刚刚"), }; } function extractAssets(payload: unknown): ServerAssetItem[] { if (Array.isArray(payload)) return payload.map(normalizeAsset); if (!isRecord(payload)) return []; const rows = payload.assets ?? payload.items; return Array.isArray(rows) ? rows.map(normalizeAsset) : []; } export const assetClient = { async list(params?: { type?: string; q?: string; status?: string }): Promise { const search = new URLSearchParams(); if (params?.type && params.type !== "all") search.set("type", params.type); if (params?.q) search.set("q", params.q); if (params?.status) search.set("status", params.status); const query = search.toString(); return extractAssets(await serverRequest(`assets${query ? `?${query}` : ""}`)); }, async create(input: CreateAssetInput): Promise { const payload = await serverRequest<{ asset: unknown }>("assets", { method: "POST", body: input, }); return normalizeAsset(payload.asset ?? payload); }, async delete(id: string, options?: DeleteAssetOptions): Promise { const path = options?.cleanupUserData ? `assets/${encodeURIComponent(id)}?cleanupUserData=1` : `assets/${encodeURIComponent(id)}`; await serverRequest(path, { method: "DELETE" }); }, };