import { useCallback, useEffect, useState } from "react"; import type { AdminDiagnosticsResponse, AdminServicesStorageResponse } from "@dada/shared-contracts"; import "./admin-services-storage.css"; const serviceLabels: Record = { ai_gateway: "AI 网关", amap: "高德", api: "API", asset_root: "素材根", resend: "Resend", worker: "Worker", }; const statusLabels: Record = { active: "正常", degraded: "有异常", disabled: "已停用", paused_provider: "供应商暂停", paused_quota: "额度暂停", unavailable: "不可用", }; const impactLabels: Record = { account: "账户模型", api: "后台接口", authentication: "认证", generation: "生成", location: "定位", model: "单模型", none: "无", storage: "存储", unknown: "未知范围", }; function formatTime(value: string | null) { if (!value) return "未记录"; return new Intl.DateTimeFormat("zh-CN", { dateStyle: "short", timeStyle: "short" }).format(new Date(value)); } async function getJson(url: string) { const response = await fetch(url, { credentials: "same-origin" }); if (response.status === 401) window.dispatchEvent(new Event("dada:session-invalid")); if (!response.ok) throw new Error("admin_state_unavailable"); return await response.json() as T; } export function AdminServicesStoragePage() { const [state, setState] = useState(); const [diagnostics, setDiagnostics] = useState(); const [loading, setLoading] = useState(true); const [failed, setFailed] = useState(false); const [copied, setCopied] = useState(false); const load = useCallback(async () => { setLoading(true); setFailed(false); try { const [nextState, nextDiagnostics] = await Promise.all([ getJson("/api/v1/admin/services-storage"), getJson("/api/v1/admin/diagnostics"), ]); setState(nextState); setDiagnostics(nextDiagnostics); } catch { setFailed(true); } finally { setLoading(false); } }, []); useEffect(() => { void load(); }, [load]); async function copyDiagnostics() { if (!diagnostics) return; try { await navigator.clipboard.writeText(diagnostics.diagnostic_text); setCopied(true); window.setTimeout(() => setCopied(false), 1800); } catch { setCopied(false); } } return (

OPERATIONS / HEALTH

服务与存储

{state ? : null}
{loading && !state ?
: null} {failed ?
状态暂时无法读取{state ? `,保留 ${formatTime(state.generated_at)} 的结果` : ""}。
: null} {state ? ( <>

SERVICE STATUS

外部服务与本机组件

仅显示脱敏状态
{state.services.map((service) => (
{serviceLabels[service.service_id]}{statusLabels[service.status]}
配置状态
{service.configured ? "已配置" : "未配置"}
影响范围
{impactLabels[service.impact_scope]}
最近检查
{formatTime(service.checked_at)}
{service.pause_reason ?
安全原因
{service.pause_reason}
: null}
))}

LOCAL DATA ROOT

本机内容容量

{state.storage.status === "active" ? "可写" : state.storage.status === "full" ? "已满" : "不可用"}
已用内容{(state.storage.managed_content_bytes / 1024 / 1024 / 1024).toFixed(2)} GB
固定上限{(state.storage.hard_limit_bytes / 1024 / 1024 / 1024).toFixed(2)} GB
容量提醒{state.storage.capacity_notice_level}
清理队列{state.storage.cleanup_pending_count}

测试数据仅保存在本机,不自动备份,也不会迁移到正式系统。当前 Windows 用户的 Dada 本机数据目录引用:{state.storage.data_root_ref}。只读规范素材库不计入 5 GB 内容额度。

最后计量
{formatTime(state.storage.last_measured_at)}
重新计量
{state.storage.remeasurement_required ? "需要完成" : "无需等待"}

DIAGNOSTICS

脱敏诊断

诊断内容只包含固定版本、组件状态、逻辑位置和容量计量,不包含密钥、邮箱、绝对路径、提示词、图片或供应商原文。

{diagnostics ?
{diagnostics.diagnostic_text}
:
}
) : null}
); }