Files
UHD_Media_Tool/apps/web/app/api/settings.ts
T
rootandClaude Opus 5.5 d53df25fc8 feat(agent): web.search / web.fetch via Brave Search (WEB-1)
- adapters/websearch: Brave client (X-Subscription-Token, error classes),
  result normalization with redaction/truncation, SSRF guard, HTML→text fetch
  (10s, 512KB, text/html|text/plain only)
- modules/websearch: 10-min PG cache (region websearch), cross-process
  per-minute limit, web.fetch allowlist = URLs from the caller's latest
  web.search; POST /websearch/test
- settings.agent.webSearch (key write-only → secret_ref); settings card
  under Agent; web.* offered to the model only when enabled, plus a system
  prompt rule; outputs marked untrusted
- capability count 27 → 29; e2e E13; runbook docs/runbook/websearch.md

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-29 00:14:08 +08:00

46 lines
1.5 KiB
TypeScript

import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import {
type GlobalSettings,
type GlobalSettingsUpdate,
mergeSettings,
type WebSearchTestResult,
} from "@umt/contracts";
import { apiData } from "~/lib/api.ts";
import { qk } from "~/lib/query-keys.ts";
export function useSettings() {
return useQuery({
queryKey: qk.settings,
queryFn: ({ signal }) => apiData<GlobalSettings>("/settings", { signal }),
});
}
/** PATCH /settings,乐观更新(失败回滚) */
export function useUpdateSettings() {
const qc = useQueryClient();
return useMutation({
mutationFn: (patch: GlobalSettingsUpdate) => apiData<GlobalSettings>("/settings", { method: "PATCH", body: patch }),
onMutate: async (patch) => {
await qc.cancelQueries({ queryKey: qk.settings });
const prev = qc.getQueryData<GlobalSettings>(qk.settings);
if (prev) qc.setQueryData(qk.settings, mergeSettings(prev, patch));
return { prev };
},
onError: (_e, _p, ctx) => {
if (ctx?.prev) qc.setQueryData(qk.settings, ctx.prev);
},
onSuccess: (data) => {
qc.setQueryData(qk.settings, data);
qc.invalidateQueries({ queryKey: qk.dashboard });
},
});
}
/** POST /websearch/test:用已保存的搜索服务 Key 实际查一次 */
export function useTestWebSearch() {
return useMutation({
mutationFn: (body: { query?: string } = {}) =>
apiData<WebSearchTestResult>("/websearch/test", { method: "POST", body }),
});
}