"use client"; import { useState } from "react"; import { useToast } from "@/components/Toast"; import { CredentialsPanel } from "@/components/settings/CredentialsPanel"; import type { TestResult } from "@/components/settings/CredentialRow"; import { KimiCodeOauth } from "@/components/settings/KimiCodeOauth"; import { RoutingPanel } from "@/components/settings/RoutingPanel"; import { SECTION_OPTIONS, SettingsNav, type SettingsSection, } from "@/components/settings/SettingsNav"; import { SegmentedControl } from "@/components/ui/SegmentedControl"; import { api } from "@/lib/api/client"; import { applyProviderChange, draftsToRoutingInput, toRoutingDrafts, type RoutingDraft, } from "@/lib/settings/providers"; import type { ProvidersResponse } from "@/lib/api/types"; interface ProvidersSettingsProps { initial: ProvidersResponse; // Kimi Code OAuth 连接态(GET .../oauth/status,进页 Server Component 取)。 kimiOauth: { connected: boolean; expiresAt: string | null }; } // 设置页主体(UX §6.10):档位路由(可编辑)+ API-key 凭据行 + Kimi Code OAuth 连接区。 export function ProvidersSettings({ initial, kimiOauth, }: ProvidersSettingsProps) { const toast = useToast(); const [activeSection, setActiveSection] = useState("routing"); const [providers, setProviders] = useState(initial.providers ?? []); const [routing, setRouting] = useState( toRoutingDrafts(initial.tier_routing ?? []), ); const [savingRouting, setSavingRouting] = useState(false); const [drafts, setDrafts] = useState>({}); const [savingId, setSavingId] = useState(null); const [testingId, setTestingId] = useState(null); const [results, setResults] = useState>({}); const sectionDetail = (value: SettingsSection): string => { if (value === "routing") { return `${routing.filter((r) => r.provider && r.model).length}/3 已配置`; } if (value === "oauth") { return kimiOauth.connected ? "Kimi 已连接" : "未连接"; } return `${providers.length} 个凭据`; }; const updateRouting = (tier: string, providerId: string): void => { setRouting((prev) => prev.map((d) => d.tier === tier ? applyProviderChange(d, providerId) : d, ), ); }; const updateRoutingModel = (tier: string, model: string): void => { setRouting((prev) => prev.map((d) => (d.tier === tier ? { ...d, model } : d)), ); }; const saveRouting = async (): Promise => { setSavingRouting(true); try { const { data, error } = await api.PUT("/settings/providers", { body: { tier_routing: draftsToRoutingInput(routing) }, }); if (error || !data) { toast("保存档位路由失败,请重试", "error"); return; } setProviders(data.providers ?? []); setRouting(toRoutingDrafts(data.tier_routing ?? [])); toast("档位路由已保存", "success"); } catch { toast("保存档位路由失败,请检查后端连接", "error"); } finally { setSavingRouting(false); } }; const saveCredential = async (providerId: string): Promise => { const apiKey = (drafts[providerId] ?? "").trim(); if (!apiKey) { toast("请先输入 API Key", "error"); return; } setSavingId(providerId); try { const { data, error } = await api.PUT("/settings/providers", { body: { credentials: [{ provider: providerId, api_key: apiKey }] }, }); if (error || !data) { toast("保存凭据失败,请重试", "error"); return; } setProviders(data.providers ?? []); setDrafts((prev) => ({ ...prev, [providerId]: "" })); toast("凭据已保存", "success"); } catch { toast("保存凭据失败,请检查后端连接", "error"); } finally { setSavingId(null); } }; const testConnection = async (providerId: string): Promise => { setTestingId(providerId); try { const { data, error } = await api.POST("/settings/providers/test", { body: { provider: providerId }, }); if (error || !data) { toast("测试连接失败", "error"); return; } setResults((prev) => ({ ...prev, [providerId]: { ok: data.ok, capabilities: data.capabilities }, })); toast( data.ok ? "连接成功" : "连接未通过", data.ok ? "success" : "error", ); } catch { toast("测试连接失败,请检查后端连接", "error"); } finally { setTestingId(null); } }; const updateDraft = (providerId: string, value: string): void => { setDrafts((prev) => ({ ...prev, [providerId]: value })); }; return (
{activeSection === "routing" ? ( void saveRouting()} /> ) : null} {activeSection === "oauth" ? (
) : null} {activeSection === "keys" ? ( void saveCredential(id)} onTest={(id) => void testConnection(id)} /> ) : null}
); }