130 lines
4.1 KiB
TypeScript
130 lines
4.1 KiB
TypeScript
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<string, unknown>;
|
|
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<string, unknown>;
|
|
}
|
|
|
|
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<ServerAssetItem[]> {
|
|
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<unknown>(`assets${query ? `?${query}` : ""}`));
|
|
},
|
|
|
|
async create(input: CreateAssetInput): Promise<ServerAssetItem> {
|
|
const payload = await serverRequest<{ asset: unknown }>("assets", {
|
|
method: "POST",
|
|
body: input,
|
|
});
|
|
return normalizeAsset(payload.asset ?? payload);
|
|
},
|
|
|
|
async delete(id: string, options?: DeleteAssetOptions): Promise<void> {
|
|
const path = options?.cleanupUserData ? `assets/${encodeURIComponent(id)}?cleanupUserData=1` : `assets/${encodeURIComponent(id)}`;
|
|
await serverRequest(path, { method: "DELETE" });
|
|
},
|
|
};
|