import "server-only"; import { getAiProvidersByType } from "@/lib/db/queries"; import { TripoApiError, tripoRequest } from "./client"; import type { TripoProviderConfig } from "./types"; const DEFAULT_TRIPO_BASE_URL = "https://api.tripo3d.ai/v2/openapi"; let tripoProviderCursor = 0; function sanitizeTripoApiKey(value: string): string { return String(value || "") .replace(/[\u2028\u2029]/g, "") .replace(/[\u0000-\u001F\u007F]/g, "") .replace(/\s+/g, "") .trim(); } function sanitizeTripoBaseUrl(value: string): string { return String(value || "") .replace(/[\u2028\u2029]/g, "") .replace(/[\u0000-\u001F\u007F]/g, "") .trim(); } function isTripoTaskNotFoundError(error: unknown): error is TripoApiError { if (!(error instanceof TripoApiError)) return false; if (error.httpStatus === 404) return true; const normalizedMessage = String(error.message || "").toLowerCase(); return ( normalizedMessage.includes("task is not found") || normalizedMessage.includes("task not found") ); } export async function getTripoProviderConfigs(): Promise { const providers = await getAiProvidersByType({ type: "tripo-3d", enabledOnly: true, }); const normalizedProviders = providers .map((provider): TripoProviderConfig | null => { const providerApiKey = sanitizeTripoApiKey(String(provider.apiKey || "")); if (!providerApiKey) return null; return { apiKey: providerApiKey, baseUrl: sanitizeTripoBaseUrl(String(provider.baseUrl || "")) || DEFAULT_TRIPO_BASE_URL, providerId: provider.id, providerName: provider.name, }; }) .filter((item): item is TripoProviderConfig => Boolean(item)); if (normalizedProviders.length > 0) { return normalizedProviders; } const envApiKey = sanitizeTripoApiKey(String(process.env.TRIPO_API_KEY || "")); if (!envApiKey) { return []; } return [ { apiKey: envApiKey, baseUrl: sanitizeTripoBaseUrl(String(process.env.TRIPO_BASE_URL || "")) || DEFAULT_TRIPO_BASE_URL, providerName: "TRIPO_API_KEY", }, ]; } export async function getTripoProviderConfig( preferredProviderId?: string | null, ): Promise { const providers = await getTripoProviderConfigs(); const normalizedPreferredProviderId = String(preferredProviderId || "").trim(); if (normalizedPreferredProviderId) { const matchedProvider = providers.find( (provider) => provider.providerId === normalizedPreferredProviderId, ); if (matchedProvider) return matchedProvider; } const tripoProvider = providers.length > 0 ? providers[tripoProviderCursor % providers.length] : null; if (providers.length > 0) { tripoProviderCursor = (tripoProviderCursor + 1) % providers.length; } if (tripoProvider) return tripoProvider; throw new Error("未配置 Tripo3D API,请在系统设置-本地AI新增 Tripo3D 提供商"); } export async function resolveTripoProviderConfigForTaskId( taskId: string, preferredProviderId?: string | null, ): Promise { const normalizedTaskId = String(taskId || "").trim(); if (!normalizedTaskId) { return getTripoProviderConfig(preferredProviderId); } const providers = await getTripoProviderConfigs(); if (providers.length <= 1) { return getTripoProviderConfig(preferredProviderId); } const normalizedPreferredProviderId = String(preferredProviderId || "").trim(); const preferredProvider = normalizedPreferredProviderId ? providers.find((provider) => provider.providerId === normalizedPreferredProviderId) : null; const orderedProviders = preferredProvider ? [ preferredProvider, ...providers.filter( (provider) => provider.providerId !== preferredProvider.providerId, ), ] : providers; let lastTaskNotFoundError: TripoApiError | null = null; let lastUnexpectedError: unknown = null; for (const provider of orderedProviders) { try { await tripoRequest>(provider, { method: "GET", path: `/task/${encodeURIComponent(normalizedTaskId)}`, }); return provider; } catch (error) { if (isTripoTaskNotFoundError(error)) { lastTaskNotFoundError = error; continue; } lastUnexpectedError = error; } } if (lastUnexpectedError) { throw lastUnexpectedError; } if (lastTaskNotFoundError) { throw lastTaskNotFoundError; } return getTripoProviderConfig(preferredProviderId); }