/** * @file PrivacySettings.tsx * @description Settings → Data → Privacy: the two opt-in ingest controls of * `server/lib/privacy.js` — redact secrets (API keys, tokens, bearer headers, * private keys, `password=…`) from the payloads and summaries AgentHub stores, * and a list of folders whose hooks are dropped whole (one absolute path per * line; a folder and everything under it). Both persist through * `PUT /api/settings/privacy`; one the environment set is shown read-only. * @author Nguyễn Ngọc Trí Vĩ */ import { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; import { AlertTriangle, ShieldCheck } from "lucide-react"; import { api } from "../../lib/api"; import type { PrivacySettings as PrivacyState } from "../../lib/api"; import { SettingsSection, SettingsSwitchRow } from "./SettingsRow"; const lines = (text: string) => text .split("\n") .map((l) => l.trim()) .filter(Boolean); export function PrivacySettings() { const { t } = useTranslation("settings"); const [state, setState] = useState(null); const [draft, setDraft] = useState(""); const [saving, setSaving] = useState(false); const [error, setError] = useState(null); const apply = (s: PrivacyState) => { setState(s); setDraft(s.ignore_cwd.join("\n")); }; useEffect(() => { api.settings.privacy .get() .then(apply) .catch(() => undefined); }, []); async function save(patch: { redact_secrets?: boolean; ignore_cwd?: string[] }) { setSaving(true); setError(null); try { apply(await api.settings.privacy.save(patch)); } catch (err) { setError(err instanceof Error ? err.message : String(err)); } finally { setSaving(false); } } const listChanged = !!state && lines(draft).join("\n") !== state.ignore_cwd.join("\n"); return (
{t("privacy.title")} } description={t("privacy.description")} > void save({ redact_secrets: v })} />

{t("privacy.ignoreHint")}