Add control monitor Start All for stopped fleet nodes.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-08-02 14:52:56 +08:00
parent 4059d826e1
commit 92c2e89b0e
2 changed files with 99 additions and 0 deletions
+60
View File
@@ -238,6 +238,7 @@ export default function MonitorPage() {
const [nodes, setNodes] = useState<NodeCard[]>(() => cachedNodes);
const [err, setErr] = useState("");
const [busy, setBusy] = useState<Record<number, string>>({});
const [batchBusy, setBatchBusy] = useState(false);
const [selected, setSelected] = useState<Set<number>>(new Set());
const [sseSec, setSseSec] = useState(cachedSseSec);
const [liveState, setLiveState] = useState<"connecting" | "live" | "retry">(
@@ -494,6 +495,56 @@ export default function MonitorPage() {
await batchUpdate(updatableIds);
}
const startableIds = nodes
.filter((n) => {
if (!n.token_configured) return false;
const s = pickStrategy(n);
const running = s.running === true || s.running === 1;
return !running;
})
.map((n) => n.id);
async function startAll() {
if (!startableIds.length) {
setErr("没有可启动的策略机(需已配对且当前未运行)");
return;
}
const names = nodes
.filter((n) => startableIds.includes(n.id))
.map((n) => n.name)
.join("、");
const ok = window.confirm(
`确认全部启动 ${startableIds.length} 台策略机?\n\n${names}\n\n将并行调用各机 Fleet 启动(已在运行的不会列入)。`,
);
if (!ok) return;
setErr("");
setBatchBusy(true);
try {
const r = await apiFetch<{
started?: number;
total?: number;
results?: { id: number; name?: string; ok: boolean; detail?: string }[];
}>("/api/nodes/start-batch", {
method: "POST",
body: JSON.stringify({ ids: startableIds }),
});
await refresh();
const fails = (r.results || []).filter((x) => !x.ok);
if (fails.length) {
setErr(
`启动完成 ${r.started ?? 0}/${r.total ?? startableIds.length};失败:` +
fails
.map((x) => `${x.name || x.id}${x.detail ? `(${x.detail})` : ""}`)
.join(""),
);
}
} catch (ex) {
setErr(ex instanceof Error ? ex.message : String(ex));
} finally {
setBatchBusy(false);
}
}
const detailNode = detailId != null ? nodes.find((x) => x.id === detailId) : null;
const detail = detailNode ? pickStrategy(detailNode) : null;
const detailRunning =
@@ -534,6 +585,15 @@ export default function MonitorPage() {
<button type="button" className="btn ghost" onClick={() => void refresh()}>
</button>
<button
type="button"
className="btn"
disabled={batchBusy || !startableIds.length}
onClick={() => void startAll()}
title="并行启动全部已配对且未运行的策略机"
>
{batchBusy ? "启动中…" : `全部启动 (${startableIds.length})`}
</button>
<button
type="button"
className="btn ghost"