import type { AgentGenerationProject, AgentGenerationProjectPersistedState, } from "@/types/agent"; interface ApiResponse { success: boolean; data?: T; error?: string; status?: number; } const AGENT_PROJECT_REQUEST_TIMEOUT_MS = 15_000; async function fetchAgentProjectApi( input: RequestInfo | URL, init?: RequestInit ): Promise> { const controller = new AbortController(); const timeoutId = globalThis.setTimeout( () => controller.abort(), AGENT_PROJECT_REQUEST_TIMEOUT_MS ); try { const response = await fetch(input, { ...init, signal: controller.signal, }); const result = await response.json(); if (!response.ok) result.status = response.status; return result; } finally { globalThis.clearTimeout(timeoutId); } } type CreateProjectPayload = { title?: string; description?: string | null; thumbnail?: string | null; projectState?: AgentGenerationProjectPersistedState; }; const pendingCreations = new Map>; }>(); function pendingCreationStorage(key: string, value?: string | null) { try { if (typeof sessionStorage === "undefined") return null; if (value === undefined) return sessionStorage.getItem(key); if (value === null) sessionStorage.removeItem(key); else sessionStorage.setItem(key, value); } catch { // Keep the in-memory request identity when browser storage is unavailable. } return null; } function getAgentProjectRequestErrorMessage(error: unknown, fallback: string) { if (error instanceof DOMException && error.name === "AbortError") { return `${fallback}:请求超时`; } return fallback; } export const agentProjectService = { async getProjects(): Promise> { try { return await fetchAgentProjectApi( "/api/agent-projects", { cache: "no-store" } ); } catch (error) { return { success: false, error: getAgentProjectRequestErrorMessage( error, "获取生图项目列表失败" ), }; } }, async getProject(id: string): Promise> { try { return await fetchAgentProjectApi( `/api/agent-projects/${id}`, { cache: "no-store" } ); } catch (error) { return { success: false, error: getAgentProjectRequestErrorMessage(error, "获取生图项目失败"), }; } }, async createProject( payload: CreateProjectPayload, options: { creationKey?: string } = {} ): Promise> { const key = `agent-project:create:${options.creationKey || JSON.stringify(payload)}`; let pending = pendingCreations.get(key); if (!pending) { pending = { id: pendingCreationStorage(key) || crypto.randomUUID() }; pendingCreations.set(key, pending); pendingCreationStorage(key, pending.id); } if (pending.promise) return pending.promise; const current = pending; const clearPending = () => { pendingCreations.delete(key); pendingCreationStorage(key, null); }; current.promise = (async () => { try { const result = await fetchAgentProjectApi( "/api/agent-projects", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ ...payload, id: current.id }), } ); if (result.success || (result.status && result.status < 500)) { clearPending(); return result; } } catch { // A lost response does not establish whether the project was created. } const recovered = await agentProjectService.getProject(current.id); if (recovered.success && recovered.data) { clearPending(); return recovered; } if (recovered.status === 401) return recovered; return { success: false, error: "创建结果尚未确认,请重试核对或打开历史项目;重试不会重复创建。", }; })().finally(() => { current.promise = undefined; }); return current.promise; }, async updateProject( id: string, payload: { title?: string; description?: string | null; thumbnail?: string | null; projectState?: AgentGenerationProjectPersistedState; } ): Promise> { try { return await fetchAgentProjectApi( `/api/agent-projects/${id}`, { method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify(payload), } ); } catch (error) { return { success: false, error: getAgentProjectRequestErrorMessage(error, "保存生图项目失败"), }; } }, async deleteProject(id: string): Promise { try { return await fetchAgentProjectApi(`/api/agent-projects/${id}`, { method: "DELETE", }); } catch (error) { return { success: false, error: getAgentProjectRequestErrorMessage(error, "删除生图项目失败"), }; } }, };