feat: establish NekoNest Cloud control and relay

This commit is contained in:
2026-08-12 23:25:43 +08:00
commit f27606b709
222 changed files with 71456 additions and 0 deletions
+384
View File
@@ -0,0 +1,384 @@
"use client";
import { useState } from "react";
import type {
AccountRecord,
AccountDeletionRequestRecord,
BetaAccessRequestRecord,
BetaRecord,
FeedbackRecord,
GrantRecord,
LaunchGateRecord,
RetentionMaintenanceResult,
ServiceIncidentRecord,
} from "@/db/repository";
type SubmitState = { loading: boolean; message: string; error: boolean };
const idle: SubmitState = { loading: false, message: "", error: false };
async function postJson<T extends Record<string, unknown> = Record<string, unknown>>(path: string, payload: Record<string, unknown>): Promise<T & { message?: string }> {
const response = await fetch(path, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ ...payload, idempotencyKey: crypto.randomUUID() }) });
const body = (await response.json()) as T & { message?: string };
if (!response.ok) throw new Error(body.message || "操作失败");
return body;
}
export function BetaAction({ beta, blockedP0 }: { beta: BetaRecord | null; blockedP0: number }) {
const [enabled, setEnabled] = useState(beta?.state === "active");
const [capacity, setCapacity] = useState(beta?.capacity_slots?.toString() ?? "");
const [reason, setReason] = useState("");
const [state, setState] = useState(idle);
async function submit(event: React.FormEvent) {
event.preventDefault(); setState({ loading: true, message: "", error: false });
try { await postJson("/api/admin/beta", { enabled, capacitySlots: capacity ? Number(capacity) : null, reason }); setState({ loading: false, message: "新的公测政策版本已写入,页面即将刷新。", error: false }); window.setTimeout(() => window.location.reload(), 700); }
catch (error) { setState({ loading: false, message: error instanceof Error ? error.message : "操作失败", error: true }); }
}
return <form className="admin-form" onSubmit={submit}><label className="switch-row"><input type="checkbox" checked={enabled} onChange={(event) => setEnabled(event.target.checked)} /><span><strong></strong><small> P0</small></span></label>{blockedP0 > 0 && <p className="form-error" role="status"> {blockedP0} P0 使</p>}<label><span></span><input type="number" min={1} value={capacity} onChange={(event) => setCapacity(event.target.value)} placeholder="留空 = 每账户不按槽位限额" /><small></small></label><label><span></span><textarea value={reason} onChange={(event) => setReason(event.target.value)} placeholder="为什么现在调整公测政策" required /></label><Submit state={state} label="发布新公测政策版本" /></form>;
}
export function ExemptionAction({ accounts }: { accounts: AccountRecord[] }) {
const [accountId, setAccountId] = useState(accounts[0]?.id ?? "");
const [capacity, setCapacity] = useState("1");
const [endsAt, setEndsAt] = useState("");
const [reason, setReason] = useState("");
const [state, setState] = useState(idle);
async function submit(event: React.FormEvent) { event.preventDefault(); setState({ loading: true, message: "", error: false }); try { await postJson("/api/admin/exemptions", { action: "create", accountId, capacitySlots: capacity ? Number(capacity) : null, endsAt: new Date(endsAt).toISOString(), reason }); setState({ loading: false, message: "闭测邀请已签发;没有创建金额、余额或支付。", error: false }); window.setTimeout(() => window.location.reload(), 700); } catch (error) { setState({ loading: false, message: error instanceof Error ? error.message : "操作失败", error: true }); } }
return <form className="admin-form" onSubmit={submit}><label><span></span><select value={accountId} onChange={(event) => setAccountId(event.target.value)} required disabled={!accounts.length}>{accounts.length ? accounts.map((account) => <option value={account.id} key={account.id}>{account.email} · {account.display_name} · {account.id.slice(-8)}</option>) : <option value=""></option>}</select><small> ID</small></label><div className="admin-form-grid"><label><span></span><input type="number" min={1} value={capacity} onChange={(event) => setCapacity(event.target.value)} placeholder="留空 = 不按槽位限额" /></label><label><span></span><input type="datetime-local" value={endsAt} onChange={(event) => setEndsAt(event.target.value)} required /></label></div><label><span></span><textarea value={reason} onChange={(event) => setReason(event.target.value)} placeholder="例如:首批闭测用户,有效至指定日期" required /></label><Submit state={state} label="签发有期限闭测邀请" /></form>;
}
export function AccessRequestAction({ request }: { request: BetaAccessRequestRecord }) {
const [action, setAction] = useState<"approve" | "decline">("approve");
const [capacity, setCapacity] = useState(request.requested_slots);
const [endsAt, setEndsAt] = useState("");
const [response, setResponse] = useState("");
const [reason, setReason] = useState("");
const [state, setState] = useState(idle);
async function submit(event: React.FormEvent) {
event.preventDefault();
setState({ loading: true, message: "", error: false });
try {
await postJson("/api/admin/beta-access", {
requestId: request.id,
action,
capacitySlots: capacity,
endsAt: action === "approve" ? new Date(endsAt).toISOString() : "",
response,
reason,
});
setState({ loading: false, message: action === "approve" ? "申请已批准,免费闭测邀请已签发。" : "申请已拒绝,用户可在控制台看到说明。", error: false });
window.setTimeout(() => window.location.reload(), 700);
} catch (error) {
setState({ loading: false, message: error instanceof Error ? error.message : "操作失败", error: true });
}
}
return (
<form className="admin-form" onSubmit={submit}>
<div className="admin-form-grid">
<label><span></span><select value={action} onChange={(event) => setAction(event.target.value as "approve" | "decline")}><option value="approve"></option><option value="decline"></option></select></label>
{action === "approve" && <label><span></span><input type="number" min={1} max={3} value={capacity} onChange={(event) => setCapacity(Number(event.target.value))} required /></label>}
</div>
{action === "approve" && <label><span></span><input type="datetime-local" value={endsAt} onChange={(event) => setEndsAt(event.target.value)} required /></label>}
<label><span></span><textarea minLength={2} maxLength={500} value={response} onChange={(event) => setResponse(event.target.value)} placeholder={action === "approve" ? "例如:已开放 1 台主机的免费闭测资格,有效期见资格页。" : "例如:当前测试名额有限,暂未开放;以后可以重新申请。"} required /></label>
<label><span></span><textarea maxLength={500} value={reason} onChange={(event) => setReason(event.target.value)} placeholder="审核依据;仅进入内部审计" required /></label>
<Submit state={state} label={action === "approve" ? "批准并签发邀请" : "拒绝申请"} />
</form>
);
}
export function InvitationRevokeAction({ invitation }: { invitation: GrantRecord }) {
const [confirmed, setConfirmed] = useState(false);
const [reason, setReason] = useState("");
const [state, setState] = useState(idle);
async function submit(event: React.FormEvent) {
event.preventDefault();
setState({ loading: true, message: "", error: false });
try {
await postJson("/api/admin/exemptions", {
action: "revoke",
grantId: invitation.id,
reason,
});
setState({ loading: false, message: "闭测邀请已撤销;既有主机不会被自动断开。", error: false });
window.setTimeout(() => window.location.reload(), 700);
} catch (error) {
setState({ loading: false, message: error instanceof Error ? error.message : "操作失败", error: true });
}
}
return (
<form className="admin-form invitation-revoke-form" onSubmit={submit}>
<label><span></span><textarea value={reason} onChange={(event) => setReason(event.target.value)} maxLength={500} required /></label>
<label className="switch-row"><input type="checkbox" checked={confirmed} onChange={(event) => setConfirmed(event.target.checked)} /><span><strong></strong><small> daemon </small></span></label>
<Submit state={state} label="撤销闭测邀请" disabled={!confirmed} />
</form>
);
}
export function GateAction({ gates }: { gates: LaunchGateRecord[] }) {
const [key, setKey] = useState(gates[0]?.key ?? "");
const selected = gates.find((gate) => gate.key === key);
const [status, setStatus] = useState<LaunchGateRecord["status"]>(selected?.status ?? "blocked");
const [owner, setOwner] = useState(selected?.owner ?? "");
const [evidenceUrl, setEvidenceUrl] = useState(selected?.evidence_url ?? "");
const [notes, setNotes] = useState(selected?.notes ?? "");
const [reason, setReason] = useState("");
const [state, setState] = useState(idle);
function selectGate(nextKey: string) { const gate = gates.find((item) => item.key === nextKey); setKey(nextKey); setStatus(gate?.status ?? "blocked"); setOwner(gate?.owner ?? ""); setEvidenceUrl(gate?.evidence_url ?? ""); setNotes(gate?.notes ?? ""); }
async function submit(event: React.FormEvent) { event.preventDefault(); setState({ loading: true, message: "", error: false }); try { await postJson("/api/admin/launch-gates", { key, status, owner, evidenceUrl, notes, reason }); setState({ loading: false, message: "门禁状态和证据已审计保存。", error: false }); window.setTimeout(() => window.location.reload(), 700); } catch (error) { setState({ loading: false, message: error instanceof Error ? error.message : "操作失败", error: true }); } }
return <form className="admin-form" onSubmit={submit}><label><span></span><select value={key} onChange={(event) => selectGate(event.target.value)}>{gates.map((gate) => <option value={gate.key} key={gate.key}>{gate.priority} · {gate.title}</option>)}</select></label><div className="admin-form-grid"><label><span></span><select value={status} onChange={(event) => setStatus(event.target.value as LaunchGateRecord["status"])}><option value="blocked"></option><option value="in_progress"></option><option value="passed"></option><option value="not_applicable"></option></select></label><label><span></span><input value={owner} onChange={(event) => setOwner(event.target.value)} placeholder="姓名或角色" /></label></div><label><span> URL</span><input type="url" value={evidenceUrl} onChange={(event) => setEvidenceUrl(event.target.value)} placeholder="https://…" /></label><label><span></span><textarea value={notes} onChange={(event) => setNotes(event.target.value)} placeholder="测试范围、日期和结论" /></label><label><span></span><textarea value={reason} onChange={(event) => setReason(event.target.value)} required /></label><Submit state={state} label="保存门禁状态" /></form>;
}
export function FeedbackAction({ feedback }: { feedback: FeedbackRecord }) {
const [response, setResponse] = useState("");
const [reason, setReason] = useState("");
const [state, setState] = useState(idle);
async function submit(event: React.FormEvent) {
event.preventDefault();
setState({ loading: true, message: "", error: false });
try {
await postJson("/api/admin/feedback", {
feedbackId: feedback.id,
response,
reason,
});
setState({ loading: false, message: "回复已保存,反馈已关闭。", error: false });
window.setTimeout(() => window.location.reload(), 700);
} catch (error) {
setState({
loading: false,
message: error instanceof Error ? error.message : "操作失败",
error: true,
});
}
}
return (
<form className="admin-form feedback-admin-form" onSubmit={submit}>
<label>
<span></span>
<textarea
minLength={2}
maxLength={1000}
value={response}
onChange={(event) => setResponse(event.target.value)}
required
/>
</label>
<label>
<span></span>
<textarea
maxLength={500}
value={reason}
onChange={(event) => setReason(event.target.value)}
placeholder="例如:已确认配置问题并给出恢复步骤"
required
/>
</label>
<Submit state={state} label="回复并关闭" />
</form>
);
}
export function IncidentAction() {
const [severity, setSeverity] = useState("degraded");
const [title, setTitle] = useState("");
const [message, setMessage] = useState("");
const [reason, setReason] = useState("");
const [state, setState] = useState(idle);
async function submit(event: React.FormEvent) {
event.preventDefault();
setState({ loading: true, message: "", error: false });
try {
await postJson("/api/admin/incidents", {
action: "create",
severity,
title,
message,
reason,
});
setState({ loading: false, message: "故障公告已发布到服务状态页。", error: false });
window.setTimeout(() => window.location.reload(), 700);
} catch (error) {
setState({
loading: false,
message: error instanceof Error ? error.message : "操作失败",
error: true,
});
}
}
return (
<form className="admin-form" onSubmit={submit}>
<label>
<span></span>
<select value={severity} onChange={(event) => setSeverity(event.target.value)}>
<option value="maintenance"></option>
<option value="degraded"></option>
<option value="outage"></option>
</select>
</label>
<label>
<span></span>
<input minLength={4} maxLength={80} value={title} onChange={(event) => setTitle(event.target.value)} required />
</label>
<label>
<span></span>
<textarea minLength={10} maxLength={1000} value={message} onChange={(event) => setMessage(event.target.value)} required />
</label>
<label>
<span></span>
<textarea maxLength={500} value={reason} onChange={(event) => setReason(event.target.value)} required />
</label>
<Submit state={state} label="发布服务公告" />
</form>
);
}
export function IncidentResolveAction({ incident }: { incident: ServiceIncidentRecord }) {
const [resolution, setResolution] = useState("");
const [reason, setReason] = useState("");
const [state, setState] = useState(idle);
async function submit(event: React.FormEvent) {
event.preventDefault();
setState({ loading: true, message: "", error: false });
try {
await postJson("/api/admin/incidents", {
action: "resolve",
incidentId: incident.id,
resolution,
reason,
});
setState({ loading: false, message: "恢复说明已发布。", error: false });
window.setTimeout(() => window.location.reload(), 700);
} catch (error) {
setState({
loading: false,
message: error instanceof Error ? error.message : "操作失败",
error: true,
});
}
}
return (
<form className="admin-form incident-resolve-form" onSubmit={submit}>
<label>
<span></span>
<textarea minLength={4} maxLength={500} value={resolution} onChange={(event) => setResolution(event.target.value)} required />
</label>
<label>
<span></span>
<textarea maxLength={500} value={reason} onChange={(event) => setReason(event.target.value)} required />
</label>
<Submit state={state} label="标记恢复并发布说明" />
</form>
);
}
export function RetentionAction() {
const [confirmed, setConfirmed] = useState(false);
const [reason, setReason] = useState("");
const [state, setState] = useState(idle);
async function submit(event: React.FormEvent) {
event.preventDefault();
setState({ loading: true, message: "", error: false });
try {
const body = await postJson<{ retention: RetentionMaintenanceResult }>(
"/api/admin/retention",
{ confirmed, reason },
);
const result = body.retention;
const total =
result.retiredPairingCodes +
result.deletedClaimRateWindows +
result.deletedClaimAttempts +
result.deletedIdempotencyRecords;
setState({
loading: false,
message: `清理完成:处理 ${total} 条到期技术记录,管理审计已追加。`,
error: false,
});
setConfirmed(false);
setReason("");
} catch (error) {
setState({
loading: false,
message: error instanceof Error ? error.message : "操作失败",
error: true,
});
}
}
return (
<form className="admin-form" onSubmit={submit}>
<div className="retention-scope-list">
<span> tombstone</span>
<span> 24 </span>
<span> 30 </span>
<span> </span>
</div>
<label>
<span></span>
<textarea
maxLength={500}
value={reason}
onChange={(event) => setReason(event.target.value)}
placeholder="例如:执行每周公测数据最小化维护"
required
/>
</label>
<label className="switch-row">
<input
type="checkbox"
checked={confirmed}
onChange={(event) => setConfirmed(event.target.checked)}
/>
<span><strong></strong><small></small></span>
</label>
<Submit state={state} label="执行到期数据清理" disabled={!confirmed} />
</form>
);
}
export function PurgeAction({ request }: { request: AccountDeletionRequestRecord }) {
const [confirmation, setConfirmation] = useState("");
const [reason, setReason] = useState("");
const [state, setState] = useState(idle);
async function submit(event: React.FormEvent) {
event.preventDefault();
setState({ loading: true, message: "", error: false });
try {
await postJson("/api/admin/relay-purges", {
deletion_request_id: request.id,
reason,
confirmation,
});
setState({ loading: false, message: "访问已暂停,Relay 将删除实时数据、附件和全部备份。", error: false });
window.setTimeout(() => window.location.reload(), 700);
} catch (error) {
setState({
loading: false,
message: error instanceof Error ? error.message : "操作失败",
error: true,
});
}
}
return (
<form className="admin-form" onSubmit={submit}>
<label>
<span></span>
<textarea minLength={8} maxLength={500} value={reason} onChange={(event) => setReason(event.target.value)} required />
</label>
<label>
<span> DELETE TENANT DATA </span>
<input value={confirmation} onChange={(event) => setConfirmation(event.target.value)} autoComplete="off" required />
<small> Engine Relay SQLite</small>
</label>
<Submit state={state} label="永久逻辑删除 Relay 数据" disabled={confirmation !== "DELETE TENANT DATA" || reason.trim().length < 8} />
</form>
);
}
function Submit({ state, label, disabled = false }: { state: SubmitState; label: string; disabled?: boolean }) { return <><button className="button button-primary" type="submit" disabled={state.loading || disabled}>{state.loading ? "正在保存…" : label}</button>{state.message && <p className={state.error ? "form-error" : "form-success"} role="status">{state.message}</p>}</>; }
+283
View File
@@ -0,0 +1,283 @@
import { requireAdminViewer } from "../cloud-auth";
import { DashboardShell, PageHeading, StatusPill, formatDate } from "../components/Shells";
import { getAdminSnapshot, type AccountDeletionRequestRecord, type FeedbackCategory } from "@/db/repository";
import type { ControlPlaneContactState } from "@/db/device-control-plane";
import { deriveInvitationDisplayState } from "@/db/invitations";
import {
AccessRequestAction,
BetaAction,
ExemptionAction,
FeedbackAction,
GateAction,
IncidentAction,
IncidentResolveAction,
InvitationRevokeAction,
PurgeAction,
RetentionAction,
} from "./AdminActions";
export const dynamic = "force-dynamic";
const feedbackCategoryLabels: Record<FeedbackCategory, string> = {
connection_issue: "连接或接入",
bug: "功能异常",
suggestion: "使用建议",
other: "其他",
};
const daemonContactCopy: Record<
ControlPlaneContactState,
{ label: string; tone: "good" | "warn" | "danger" | "neutral" }
> = {
fresh: { label: "控制面正常", tone: "good" },
delayed: { label: "联系延迟", tone: "warn" },
stale: { label: "长时间未联系", tone: "danger" },
never: { label: "尚未联系", tone: "neutral" },
invalid: { label: "时间异常", tone: "danger" },
};
const deletionStatusCopy: Record<
AccountDeletionRequestRecord["status"],
{ label: string; tone: "good" | "warn" | "danger" | "neutral" }
> = {
requested: { label: "待核对", tone: "warn" },
cancelled: { label: "已撤回", tone: "neutral" },
processing: { label: "删除中", tone: "danger" },
relay_purged: { label: "Relay 已逻辑删除", tone: "good" },
};
const retentionStatusCopy = {
never_run: {
label: "尚未自动运行",
tone: "warn",
detail: "定时入口尚未留下首次执行证据;管理员仍可使用下方手工回退。",
},
running: {
label: "正在清理",
tone: "info",
detail: "自动任务已经取得单实例运行权,正在处理已到期技术记录。",
},
healthy: {
label: "自动清理正常",
tone: "good",
detail: "最近一次自动清理在预期时间窗内成功完成。",
},
overdue: {
label: "自动清理逾期",
tone: "warn",
detail: "最近成功已经超过 36 小时,请核对托管环境的定时触发器。",
},
failed: {
label: "自动清理失败",
tone: "danger",
detail: "最近一次自动清理失败;错误码已最小化保存,运行时错误会继续进入平台观测。",
},
stalled: {
label: "自动清理卡住",
tone: "danger",
detail: "任务运行超过 30 分钟;下一次触发可以安全接管,但应先核对 D1 与 Worker 状态。",
},
invalid: {
label: "清理记录异常",
tone: "danger",
detail: "运行状态包含无效时间、计数或触发来源,不能把它当作成功证据。",
},
} as const;
function formatPercent(value: number | null) {
return value === null ? "暂无样本" : `${value}%`;
}
function formatDuration(seconds: number | null) {
if (seconds === null) return "暂无样本";
if (seconds < 60) return `${seconds}`;
if (seconds < 3600) return `${Math.round(seconds / 60)} 分钟`;
if (seconds < 86400) return `${Math.round((seconds / 3600) * 10) / 10} 小时`;
return `${Math.round((seconds / 86400) * 10) / 10}`;
}
export default async function AdminPage() {
const viewer = await requireAdminViewer();
const snapshot = await getAdminSnapshot();
const deferredPaidGates = snapshot.gates.filter((gate) => gate.priority === "PAID").length;
const openFeedback = snapshot.feedback.filter((item) => item.status === "open");
const pendingAccessRequests = snapshot.accessRequests.filter((item) => item.status === "requested");
const pendingAccessAccountIds = new Set(pendingAccessRequests.map((item) => item.account_id));
const proactiveInvitationAccounts = snapshot.accounts.filter((account) => !pendingAccessAccountIds.has(account.id));
const invitations = snapshot.grants.filter((grant) => grant.source === "admin_exemption");
const activeIncidents = snapshot.incidents.filter((item) => item.status === "active");
const pendingDeletionRequests = snapshot.deletionRequests.filter((item) => item.status === "requested");
const daemonContactAttention = snapshot.hostContacts.delayed
+ snapshot.hostContacts.stale
+ snapshot.hostContacts.never
+ snapshot.hostContacts.invalid;
const daemonContactTone = snapshot.hostContacts.invalid || snapshot.hostContacts.stale
? "danger"
: daemonContactAttention
? "warn"
: snapshot.hostContacts.totalActive
? "good"
: "neutral";
const retentionStatus = retentionStatusCopy[snapshot.retention.state];
const retentionLastSuccess = snapshot.retention.state === "invalid"
? "记录异常"
: formatDate(snapshot.retention.lastSuccessAt, true);
return (
<DashboardShell viewer={viewer} active="/admin">
<div className="cloud-page admin-page">
<PageHeading eyebrow="PUBLIC BETA ADMIN / 公测后台" title="免费政策、接入资格与上线门禁。" description="当前只运营免费公测。每次管理变更仍需理由、幂等键和追加式审计;报价、订单和价格发布均保持关闭。" />
<section className="admin-metrics"><article><span> P0 </span><strong>{snapshot.blockedP0}</strong><small></small></article><article><span></span><strong>{snapshot.beta?.state === "active" ? "全部免费" : "已结束"}</strong><small>{snapshot.beta?.capacity_slots === null ? "每账户不按主机槽位限额" : `每账户最多 ${snapshot.beta?.capacity_slots ?? 0} 台主机`}</small></article><article><span></span><strong>{activeIncidents.length ? `${activeIncidents.length} 个事件` : "正常"}</strong><small></small></article><article><span></span><strong>{pendingAccessRequests.length}</strong><small> {openFeedback.length} {deferredPaidGates} </small></article></section>
<section className="panel full-panel operations-panel">
<div className="panel-heading">
<div><span>DAEMON CONTROL PLANE / </span><h2></h2></div>
<StatusPill tone={daemonContactTone}>{daemonContactAttention ? `${daemonContactAttention} 台需留意` : snapshot.hostContacts.totalActive ? "全部正常" : "暂无主机"}</StatusPill>
</div>
<p className="operations-intro"> Relay daemon sealed </p>
<div className="operations-grid daemon-contact-grid">
<article><span></span><strong>{snapshot.hostContacts.totalActive}</strong><small>{snapshot.hostContacts.versionUnknown} daemon </small></article>
<article><span></span><strong>{snapshot.hostContacts.fresh}</strong><small>使 Relay </small></article>
<article><span></span><strong>{snapshot.hostContacts.delayed}</strong><small></small></article>
<article><span></span><strong>{snapshot.hostContacts.stale}</strong><small> daemon </small></article>
<article><span></span><strong>{snapshot.hostContacts.never}</strong><small> Relay </small></article>
<article><span></span><strong>{snapshot.hostContacts.invalid}</strong><small></small></article>
</div>
<div className="daemon-contact-attention">
<h3></h3>
{snapshot.hostContacts.attentionHosts.length ? (
<div className="admin-gate-table">
{snapshot.hostContacts.attentionHosts.map((host) => {
const state = daemonContactCopy[host.contact_state];
return (
<article key={host.id}>
<span className="gate-priority">{host.os === "windows" ? "W" : "L"}</span>
<div><strong>{host.name}</strong><small>{host.email} · {host.id} · {host.daemon_version || "版本待上报"}</small></div>
<StatusPill tone={state.tone}>{state.label}</StatusPill>
<span>{host.control_plane_last_seen_at ? formatDate(host.control_plane_last_seen_at, true) : "没有成功查询"}</span>
</article>
);
})}
</div>
) : <div className="empty-inline"></div>}
</div>
<p className="operations-footnote"> 25 {formatDate(snapshot.hostContacts.generatedAt, true)}</p>
</section>
<section className="panel full-panel operations-panel">
<div className="panel-heading">
<div><span>ROLLING 30 DAYS / 30 </span><h2></h2></div>
<StatusPill tone="info"></StatusPill>
</div>
<p className="operations-intro"> relay </p>
<div className="operations-grid">
<article>
<span></span>
<strong>{snapshot.operations.accounts.newAccounts}</strong>
<small> {snapshot.operations.accounts.total} {snapshot.operations.accounts.withActiveHost} </small>
</article>
<article>
<span></span>
<strong>{snapshot.operations.accessRequests.submitted}</strong>
<small>{snapshot.operations.accessRequests.pending} {snapshot.operations.accessRequests.cancelled} </small>
</article>
<article>
<span></span>
<strong>{formatPercent(snapshot.operations.accessRequests.approvalRatePercent)}</strong>
<small>{snapshot.operations.accessRequests.approved}/{snapshot.operations.accessRequests.decided} {formatDuration(snapshot.operations.accessRequests.averageReviewSeconds)}</small>
</article>
<article>
<span></span>
<strong>{snapshot.operations.accessRequests.requestedSlotDemand} </strong>
<small> {snapshot.operations.accessRequests.averageRequestedSlots ?? "暂无样本"} Windows {snapshot.operations.accessRequests.windows}Linux {snapshot.operations.accessRequests.linux} {snapshot.operations.accessRequests.both}</small>
</article>
<article>
<span></span>
<strong>{formatPercent(snapshot.operations.accessRequests.postApprovalClaimRatePercent)}</strong>
<small>{snapshot.operations.accessRequests.approvedWithPostApprovalClaim}/{snapshot.operations.accessRequests.approved} relay </small>
</article>
<article>
<span></span>
<strong>{formatPercent(snapshot.operations.pairings.claimRatePercent)}</strong>
<small>{snapshot.operations.pairings.claimed}/{snapshot.operations.pairings.created} {formatDuration(snapshot.operations.pairings.averageClaimSeconds)}</small>
</article>
<article>
<span></span>
<strong>{snapshot.operations.pairings.attentionRequired}</strong>
<small>{snapshot.operations.pairings.waiting} {snapshot.operations.pairings.expired} {snapshot.operations.pairings.locked} {snapshot.operations.pairings.rejectedAttempts} {snapshot.operations.pairings.rateLimitedAttempts} </small>
</article>
<article>
<span>Relay placement </span>
<strong>{formatPercent(snapshot.operations.provisioning.successRatePercent)}</strong>
<small>{snapshot.operations.provisioning.succeeded}/{snapshot.operations.provisioning.created} placement active {formatDuration(snapshot.operations.provisioning.averageCompletionSeconds)}</small>
</article>
<article>
<span></span>
<strong>{formatPercent(snapshot.operations.support.resolutionRatePercent)}</strong>
<small>{snapshot.operations.support.resolved}/{snapshot.operations.support.created} {snapshot.operations.support.connectionIssues} {formatDuration(snapshot.operations.support.averageResolutionSeconds)}</small>
</article>
<article className="operations-unavailable">
<span></span>
<strong></strong>
<small>relay </small>
</article>
</div>
<p className="operations-footnote"> {snapshot.operations.windowDays} {formatDate(snapshot.operations.generatedAt, true)}</p>
</section>
<div className="admin-grid">
<section className="panel admin-module"><div className="panel-heading"><div><span></span><h2></h2></div><StatusPill tone={snapshot.beta?.state === "active" && snapshot.blockedP0 === 0 ? "good" : snapshot.beta?.state === "active" ? "warn" : "neutral"}>{snapshot.beta?.state === "active" && snapshot.blockedP0 > 0 ? "政策已设 / 接入冻结" : snapshot.beta?.state ?? "未设置"}</StatusPill></div><BetaAction beta={snapshot.beta} blockedP0={snapshot.blockedP0} /></section>
<section className="panel admin-module"><div className="panel-heading"><div><span></span><h2></h2></div><StatusPill tone="neutral"></StatusPill></div><p></p></section>
<section className="panel admin-module"><div className="panel-heading"><div><span> / </span><h2></h2></div></div><p></p><ExemptionAction accounts={proactiveInvitationAccounts} /></section>
<section className="panel admin-module">
<div className="panel-heading">
<div><span>DATA MINIMIZATION</span><h2></h2></div>
<StatusPill tone={retentionStatus.tone}>{retentionStatus.label}</StatusPill>
</div>
<p> 02:17 退</p>
<div className="maintenance-status" aria-label="到期数据自动清理状态">
<div><span></span><strong>{retentionLastSuccess}</strong></div>
<div><span></span><strong>{snapshot.retention.runCount}</strong></div>
<div><span></span><strong>{snapshot.retention.consecutiveFailures}</strong></div>
</div>
<p className="maintenance-status-detail">
{retentionStatus.detail}
{snapshot.retention.ageSeconds !== null ? ` 距相关状态约 ${formatDuration(snapshot.retention.ageSeconds)}` : ""}
{snapshot.retention.errorCode ? ` 错误码:${snapshot.retention.errorCode}` : ""}
</p>
<RetentionAction />
</section>
<section className="panel admin-module"><div className="panel-heading"><div><span></span><h2>线</h2></div></div><GateAction gates={snapshot.gates} /></section>
</div>
<section className="panel full-panel"><div className="panel-heading"><div><span>CLOSED BETA REQUESTS</span><h2></h2></div><StatusPill tone={pendingAccessRequests.length ? "warn" : "good"}>{pendingAccessRequests.length ? `${pendingAccessRequests.length} 条待处理` : "已清空"}</StatusPill></div><p> 13 </p><div className="admin-feedback-list">{snapshot.accessRequests.length ? snapshot.accessRequests.map((request) => { const account = snapshot.accounts.find((item) => item.id === request.account_id); return <article key={request.id}><div className="feedback-meta"><StatusPill tone={request.status === "approved" ? "good" : request.status === "requested" ? "warn" : "neutral"}>{request.status === "approved" ? "已批准" : request.status === "requested" ? "待处理" : request.status === "declined" ? "未批准" : "用户已撤回"}</StatusPill><span>{account?.email ?? request.account_id}</span><span>{request.requested_slots} · {request.preferred_os}</span><span>{formatDate(request.requested_at, true)}</span></div><p>{request.use_case}</p>{request.admin_response && <div className="feedback-response"><strong></strong><p>{request.admin_response}</p></div>}<code>{request.id}</code>{request.status === "requested" && <AccessRequestAction request={request} />}</article>; }) : <div className="empty-inline"></div>}</div></section>
<section className="panel full-panel"><div className="panel-heading"><div><span>CLOSED BETA INVITATIONS</span><h2></h2></div><StatusPill tone={invitations.some((grant) => deriveInvitationDisplayState(grant) === "active") ? "good" : "neutral"}>{invitations.filter((grant) => deriveInvitationDisplayState(grant) === "active").length} </StatusPill></div><p></p><div className="admin-feedback-list">{invitations.length ? invitations.map((invitation) => { const state = deriveInvitationDisplayState(invitation); const account = snapshot.accounts.find((item) => item.id === invitation.account_id); return <article key={invitation.id}><div className="feedback-meta"><StatusPill tone={state === "active" ? "good" : "neutral"}>{state === "active" ? "有效" : state === "expired" ? "已到期" : "已撤销"}</StatusPill><span>{account?.email ?? invitation.account_id}</span><span>{invitation.capacity_slots === null ? "不按槽位限额" : `${invitation.capacity_slots} 个槽位`}</span><span> {formatDate(invitation.ends_at, true)}</span></div><p>{invitation.reason}</p><code>{invitation.id}</code>{state === "active" && <InvitationRevokeAction invitation={invitation} />}</article>; }) : <div className="empty-inline"></div>}</div></section>
<section className="panel full-panel">
<div className="panel-heading">
<div><span>ACCOUNT LIFECYCLE</span><h2></h2></div>
<StatusPill tone={pendingDeletionRequests.length ? "warn" : "good"}>
{pendingDeletionRequests.length ? `${pendingDeletionRequests.length} 条待核对` : "无待核对"}
</StatusPill>
</div>
<p>Relay </p>
<div className="admin-deletion-list">
{snapshot.deletionRequests.length ? snapshot.deletionRequests.map((request) => {
const copy = deletionStatusCopy[request.status];
return (
<article key={request.id}>
<div>
<StatusPill tone={copy.tone}>{copy.label}</StatusPill>
<strong>{snapshot.accounts.find((account) => account.id === request.account_id)?.email ?? request.account_id}</strong>
<span>{formatDate(request.requested_at, true)}</span>
</div>
<p>{request.reason || "用户未填写补充说明。"}</p>
<code>{request.id}</code>
{request.status === "requested" && <PurgeAction request={request} />}
</article>
);
}) : <div className="empty-inline"></div>}
</div>
</section>
<section className="panel full-panel"><div className="panel-heading"><div><span>STATUS & INCIDENTS</span><h2></h2></div><StatusPill tone={activeIncidents.length ? "danger" : "good"}>{activeIncidents.length ? `${activeIncidents.length} 个处理中` : "服务正常"}</StatusPill></div><div className="admin-incident-layout"><div><h3></h3><p></p><IncidentAction /></div><div className="admin-incident-list"><h3></h3>{snapshot.incidents.length ? snapshot.incidents.map((incident) => <article key={incident.id}><div className="incident-card-heading"><StatusPill tone={incident.status === "resolved" ? "good" : incident.severity === "outage" ? "danger" : incident.severity === "degraded" ? "warn" : "info"}>{incident.status === "resolved" ? "已恢复" : incident.severity === "outage" ? "服务中断" : incident.severity === "degraded" ? "服务降级" : "计划维护"}</StatusPill><span>{formatDate(incident.started_at, true)}</span></div><h4>{incident.title}</h4><p>{incident.message}</p>{incident.status === "active" ? <IncidentResolveAction incident={incident} /> : <div className="feedback-response"><strong></strong><p>{incident.resolution}</p></div>}</article>) : <div className="empty-inline"></div>}</div></div></section>
<section className="panel full-panel"><div className="panel-heading"><div><span>FREE BETA SUPPORT</span><h2></h2></div><StatusPill tone={openFeedback.length ? "warn" : "good"}>{openFeedback.length ? `${openFeedback.length} 条待处理` : "已清空"}</StatusPill></div>{snapshot.feedback.length ? <div className="admin-feedback-list">{snapshot.feedback.map((item) => <article key={item.id}><div className="feedback-meta"><StatusPill tone={item.status === "resolved" ? "good" : "warn"}>{item.status === "resolved" ? "已回复" : "待处理"}</StatusPill><span>{feedbackCategoryLabels[item.category]}</span><span>{formatDate(item.created_at, true)}</span><span>{snapshot.accounts.find((account) => account.id === item.account_id)?.email ?? item.account_id}</span></div><p>{item.message}</p>{item.admin_response && <div className="feedback-response"><strong></strong><p>{item.admin_response}</p></div>}{item.status === "open" && <FeedbackAction feedback={item} />}</article>)}</div> : <div className="empty-inline"></div>}</section>
<section className="panel full-panel"><div className="panel-heading"><div><span>P0 / P1 / PAID</span><h2></h2></div></div><div className="admin-gate-table">{snapshot.gates.map((gate) => <article key={gate.key}><span className="gate-priority">{gate.priority}</span><div><strong>{gate.title}</strong><small>{gate.category} · {gate.key}</small></div><StatusPill tone={gate.priority === "PAID" ? "neutral" : gate.status === "passed" ? "good" : gate.status === "in_progress" ? "warn" : "danger"}>{gate.priority === "PAID" ? "以后处理" : gate.status}</StatusPill><span>{gate.owner || "待指定"}</span></article>)}</div></section>
<section className="panel full-panel"><div className="panel-heading"><div><span>APPEND ONLY</span><h2></h2></div></div><div className="audit-list">{snapshot.audits.length ? snapshot.audits.map((audit) => <article key={audit.id}><span>{formatDate(audit.created_at, true)}</span><strong>{audit.action}</strong><span>{audit.target_type} / {audit.target_id}</span><p>{audit.reason}</p><code>{audit.actor_id}</code></article>) : <div className="empty-inline"></div>}</div></section>
</div>
</DashboardShell>
);
}