Просмотр исходного кода

fix(studio): rehydrate saved service keys in detail view

Ma 6 месяцев назад
Родитель
Сommit
a938318f21

+ 15 - 0
packages/studio/src/api/server.test.ts

@@ -682,6 +682,21 @@ describe("createStudioServer daemon lifecycle", () => {
     });
   });
 
+  it("returns stored service secret for detail page rehydration", async () => {
+    loadSecretsMock.mockResolvedValue({
+      services: {
+        moonshot: { apiKey: "sk-moon" },
+      },
+    });
+
+    const { createStudioServer } = await import("./server.js");
+    const app = createStudioServer(cloneProjectConfig() as never, root);
+
+    const response = await app.request("http://localhost/api/v1/services/moonshot/secret");
+    expect(response.status).toBe(200);
+    await expect(response.json()).resolves.toEqual({ apiKey: "sk-moon" });
+  });
+
   it("rejects create requests when a complete book with the same id already exists", async () => {
     await mkdir(join(root, "books", "existing-book", "story"), { recursive: true });
     await writeFile(join(root, "books", "existing-book", "book.json"), JSON.stringify({ id: "existing-book" }), "utf-8");

+ 8 - 0
packages/studio/src/api/server.ts

@@ -870,6 +870,14 @@ export function createStudioServer(initialConfig: ProjectConfig, root: string) {
     return c.json({ ok: true });
   });
 
+  app.get("/api/v1/services/:service/secret", async (c) => {
+    const service = c.req.param("service");
+    const secrets = await loadSecrets(root);
+    return c.json({
+      apiKey: secrets.services[service]?.apiKey ?? "",
+    });
+  });
+
   app.get("/api/v1/services/:service/models", async (c) => {
     const service = c.req.param("service");
     const apiKey = c.req.query("apiKey") || await getServiceApiKey(root, service);

+ 13 - 0
packages/studio/src/pages/ServiceDetailPage.tsx

@@ -106,6 +106,19 @@ export function ServiceDetailPage({ serviceId, nav }: { serviceId: string; nav:
   const effectiveServiceId = isCustom ? `custom:${resolvedCustomName}` : serviceId;
   const label = isCustom ? (customName || persistedCustomName || "自定义服务") : (svc?.label ?? serviceId);
 
+  useEffect(() => {
+    let cancelled = false;
+    void fetchJson<{ apiKey?: string }>(`/services/${encodeURIComponent(effectiveServiceId)}/secret`)
+      .then((data) => {
+        if (cancelled) return;
+        if (typeof data.apiKey === "string") {
+          setApiKey(data.apiKey);
+        }
+      })
+      .catch(() => {});
+    return () => { cancelled = true; };
+  }, [effectiveServiceId]);
+
   if (loading) return <DetailSkeleton />;
 
   // -- Derived state --