Fix canvas transfer timeouts and move images out of snapshots

This commit is contained in:
2026-11-06 21:51:13 +08:00
parent af844131ed
commit 570cd53fc3
21 changed files with 460 additions and 48 deletions
+24 -4
View File
@@ -6,7 +6,12 @@ export type Profile = {
avatar_url?: string | null;
};
export class ApiError extends Error {
constructor(code: string, readonly status: number, readonly details?: unknown, readonly retryAfter?: string | null) { super(code); this.name = "ApiError"; }
readonly status: number;
readonly details?: unknown;
readonly retryAfter?: string | null;
constructor(code: string, status: number, details?: unknown, retryAfter?: string | null) {
super(code); this.name = "ApiError"; this.status = status; this.details = details; this.retryAfter = retryAfter;
}
}
export async function api<T>(
path: string,
@@ -16,10 +21,20 @@ export async function api<T>(
...options,
headers: { ...(options.body instanceof FormData ? {} : { "Content-Type": "application/json" }), ...options.headers },
});
const body = await res.json().catch(() => null);
let body: unknown;
if (res.status === 204) body = null;
else {
try { body = await res.json(); }
catch (error) {
if (options.signal?.aborted) throw options.signal.reason;
const malformed = error instanceof SyntaxError;
throw new ApiError(malformed ? "invalid_api_response" : "response_interrupted", res.ok ? 502 : res.status);
}
}
const payload = body as { error?: string | { code?: string }; details?: unknown } | null;
if (!res.ok) {
const code = body?.error?.code || body?.error;
throw new ApiError(typeof code === "string" ? code : "request_failed", res.status, body?.details, res.headers.get("retry-after"));
const code = typeof payload?.error === "object" ? payload.error?.code : payload?.error;
throw new ApiError(typeof code === "string" ? code : "request_failed", res.status, payload?.details, res.headers.get("retry-after"));
}
return body as T;
}
@@ -55,6 +70,11 @@ export const errorText = (error: unknown) => {
invalid_profile: "请检查显示名称,以及用户名是否为 3–40 位小写字母、数字、下划线或连字符。",
forbidden: "你没有在此工作空间创建或编辑图谱的权限。",
invalid_canvas: "画布数据未通过服务端校验,请导出备份后检查节点内容。",
response_interrupted: "画布数据传输中断或超时,请重新加载;当前内容未被覆盖。",
invalid_api_response: "接口返回了不完整或无法解析的数据,请重新加载。",
invalid_asset: "图片格式无法识别,请选择有效的 PNG、JPEG、WebP 或 GIF。",
asset_too_large: "图片超出上传限制,请选择小于 20 MiB 的文件。",
invalid_asset_response: "图片上传结果无效,画布内容未替换,请重试。",
invalid_canvas_snapshot: "服务端画布数据格式无效,请重新读取或联系维护者。",
canvas_schema_upgrade_required: "这张画布已升级,请刷新以加载新版客户端。",
undo_receipt_expired: "撤销记录已过期,当前内容已保留。",