feat: add monitoring settings tab, workbench view, and inspection alert improvements
Some checks failed
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled
Some checks failed
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled
This commit is contained in:
@@ -138,31 +138,44 @@ const REMOTE_API_MAP: Record<string, { method: string; path: string; arrayField?
|
||||
getLatestAll: { method: "GET", path: "/api/inspection/results", arrayField: "results" },
|
||||
getLatestByDevice: { method: "GET", path: "/api/inspection/results", arrayField: "results" },
|
||||
getRecentResults: { method: "GET", path: "/api/inspection/results/recent", arrayField: "results" },
|
||||
getResultsByTimeRange: { method: "GET", path: "/api/inspection/results/range", arrayField: "results" },
|
||||
getActiveAlerts: { method: "GET", path: "/api/alerts", arrayField: "alerts" },
|
||||
listAlerts: { method: "GET", path: "/api/alerts", arrayField: "alerts" },
|
||||
getAlertStats: { method: "GET", path: "/api/alert-stats" },
|
||||
listRules: { method: "GET", path: "/api/alert-rules", arrayField: "rules" },
|
||||
getRule: { method: "GET", path: "/api/alert-rules", arrayField: "rules" },
|
||||
createRule: { method: "POST", path: "/api/alert-rule" },
|
||||
updateRule: { method: "POST", path: "/api/alert-rule" },
|
||||
deleteRule: { method: "POST", path: "/api/alert-rule" },
|
||||
toggleRule: { method: "POST", path: "/api/alert-rule" },
|
||||
getAllExtensions: { method: "GET", path: "/api/devices", arrayField: "devices" },
|
||||
getEnabledDevices: { method: "GET", path: "/api/devices/enabled", arrayField: "devices" },
|
||||
schedulerStatus: { method: "GET", path: "/api/inspection/scheduler-status" },
|
||||
getCatalog: { method: "GET", path: "/api/inspection/catalog", arrayField: "catalog" },
|
||||
getSysConfig: { method: "GET", path: "/api/inspection/sys-config" },
|
||||
updateSysConfig: { method: "POST", path: "/api/inspection/sys-config" },
|
||||
getLabels: { method: "GET", path: "/api/inspection/labels" },
|
||||
listReportSnapshots: { method: "GET", path: "/api/report/snapshots", arrayField: "snapshots" },
|
||||
isReportSnapshotRunning: { method: "GET", path: "/api/report/snapshot-status" },
|
||||
getReportSchedule: { method: "GET", path: "/api/report/schedule" },
|
||||
saveReportSchedule: { method: "POST", path: "/api/report/schedule" },
|
||||
toggleReportScheduler: { method: "POST", path: "/api/report/scheduler-toggle" },
|
||||
reportSchedulerStatus: { method: "GET", path: "/api/report/scheduler-status" },
|
||||
listDailyReports: { method: "GET", path: "/api/daily-reports", arrayField: "reports" },
|
||||
batchSetInspectionEnabled: { method: "POST", path: "/api/devices/batch-enable" },
|
||||
runReportAiSummary: { method: "POST", path: "/api/run-report-ai-summary" },
|
||||
generateDailyReport: { method: "POST", path: "/api/generate-daily-report" },
|
||||
acknowledgeAlert: { method: "POST", path: "/api/alert/__ID__/acknowledge" },
|
||||
resolveAlert: { method: "POST", path: "/api/alert/__ID__/resolve" },
|
||||
closeAlert: { method: "POST", path: "/api/alert/__ID__/close" },
|
||||
updateAlertNote: { method: "POST", path: "/api/alert/__ID__/note" },
|
||||
};
|
||||
|
||||
// 某些 API 返回数组 —— 在 REMOTE_API_MAP 里用 arrayField 标记
|
||||
// 找不到映射的 API 默认返回 null,让调用方自己兜底
|
||||
const ARRAY_METHODS = new Set([
|
||||
"listReportSnapshots", "listDailyReports", "listRules", "getLatestAll",
|
||||
"getLatestByDevice", "getRecentResults", "getActiveAlerts", "listAlerts",
|
||||
"getLatestByDevice", "getRecentResults", "getResultsByTimeRange", "getActiveAlerts", "listAlerts",
|
||||
"getAllExtensions", "getEnabledDevices", "getCatalog",
|
||||
]);
|
||||
|
||||
@@ -178,10 +191,19 @@ async function remoteFetch(name: string, ...args: any[]): Promise<any> {
|
||||
if (name === "schedulerStatus" || name === "isReportSnapshotRunning") return { running: false };
|
||||
return null;
|
||||
}
|
||||
let url = `${mapping.path}?token=${encodeURIComponent(REMOTE_TOKEN)}`;
|
||||
let url = mapping.path;
|
||||
// 告警状态变更:把 __ID__ 替换成实际 alertId
|
||||
if (url.includes("__ID__") && args[0]) {
|
||||
url = url.replace("__ID__", encodeURIComponent(args[0]));
|
||||
args = args.slice(1); // alertId 从参数里移除,剩下的是 payload
|
||||
}
|
||||
url += `?token=${encodeURIComponent(REMOTE_TOKEN)}`;
|
||||
if (name === "getLatestByDevice" && args[0]) {
|
||||
url += `&device_id=${encodeURIComponent(args[0])}`;
|
||||
}
|
||||
if (name === "getResultsByTimeRange" && args[0] !== undefined && args[1] !== undefined) {
|
||||
url += `&start=${encodeURIComponent(args[0])}&end=${encodeURIComponent(args[1])}`;
|
||||
}
|
||||
if ((name === "listAlerts" || name === "listReportSnapshots") && args[0] !== undefined) {
|
||||
const opts = typeof args[0] === "object" ? args[0] : { limit: args[0] };
|
||||
if (opts.status) url += `&status=${opts.status}`;
|
||||
@@ -204,6 +226,24 @@ async function remoteFetch(name: string, ...args: any[]): Promise<any> {
|
||||
} else if (name === "generateDailyReport") {
|
||||
// invoke("generateDailyReport", options)
|
||||
fetchOpts.body = JSON.stringify(args[0] || {});
|
||||
} else if (name === "createRule") {
|
||||
fetchOpts.body = JSON.stringify({ action: "create", data: args[0] || {} });
|
||||
} else if (name === "updateRule") {
|
||||
fetchOpts.body = JSON.stringify({ action: "update", id: args[0], updates: args[1] || {} });
|
||||
} else if (name === "deleteRule") {
|
||||
fetchOpts.body = JSON.stringify({ action: "delete", id: args[0] });
|
||||
} else if (name === "toggleRule") {
|
||||
fetchOpts.body = JSON.stringify({ action: "toggle", id: args[0], enabled: !!args[1] });
|
||||
} else if (name === "toggleReportScheduler") {
|
||||
fetchOpts.body = JSON.stringify({ enabled: !!args[0] });
|
||||
} else if (name === "acknowledgeAlert") {
|
||||
fetchOpts.body = JSON.stringify({ operator: args[0] || "remote" });
|
||||
} else if (name === "resolveAlert") {
|
||||
fetchOpts.body = JSON.stringify({ reason: args[0] || "manual", operator: args[1] || "remote" });
|
||||
} else if (name === "closeAlert") {
|
||||
fetchOpts.body = JSON.stringify({ note: args[0] || "" });
|
||||
} else if (name === "updateAlertNote") {
|
||||
fetchOpts.body = JSON.stringify({ note: args[0] || "" });
|
||||
} else {
|
||||
fetchOpts.body = JSON.stringify(args[0] !== undefined ? args[0] : {});
|
||||
}
|
||||
@@ -213,27 +253,27 @@ async function remoteFetch(name: string, ...args: any[]): Promise<any> {
|
||||
// 适配不同返回结构
|
||||
if (data && typeof data === "object") {
|
||||
if (Array.isArray(data)) return data;
|
||||
// 优先用 arrayField 约定的字段
|
||||
// getAllExtensions:必须把 devices 数组转成 Record<host_id, ext>
|
||||
if (name === "getAllExtensions" && Array.isArray(data.devices)) {
|
||||
const map: Record<string, any> = {};
|
||||
for (const d of data.devices) {
|
||||
if (d?.host_id) map[d.host_id] = d;
|
||||
}
|
||||
return map;
|
||||
}
|
||||
// reportSchedulerStatus: { running, schedule }
|
||||
if (name === "reportSchedulerStatus" && "running" in data) return data;
|
||||
// 优先用 arrayField 约定的字段(getAllExtensions 已在上文处理 Record 转换)
|
||||
if (mapping.arrayField && Array.isArray(data[mapping.arrayField])) {
|
||||
return data[mapping.arrayField];
|
||||
}
|
||||
if (data.alerts !== undefined) return data.alerts;
|
||||
if (data.devices !== undefined) {
|
||||
if (name === "getAllExtensions") {
|
||||
// getAllExtensions 返回 Record<string, DeviceExtension>
|
||||
const map: Record<string, any> = {};
|
||||
for (const d of data.devices) {
|
||||
map[d.host_id] = d;
|
||||
}
|
||||
return map;
|
||||
}
|
||||
return data.devices;
|
||||
}
|
||||
if (data.devices !== undefined) return data.devices;
|
||||
if (data.rules !== undefined) return data.rules;
|
||||
if (data.results !== undefined) return data.results;
|
||||
if (data.snapshots !== undefined) return data.snapshots;
|
||||
if (data.reports !== undefined) return data.reports;
|
||||
if (data.schedule !== undefined) return data.schedule;
|
||||
if (data.schedule !== undefined && (name === "getReportSchedule" || name === "saveReportSchedule")) return data.schedule;
|
||||
if (data.ok === true && data.data !== undefined) return data.data;
|
||||
if ("running" in data) return data; // schedulerStatus / snapshot-status
|
||||
return data;
|
||||
@@ -315,6 +355,8 @@ export const inspectionResultApi = {
|
||||
invoke("getLatestByDevice", hostId, limit),
|
||||
getRecent: (limit = 50): Promise<InspectionResult[]> =>
|
||||
invoke("getRecentResults", limit),
|
||||
getByTimeRange: (startTime: number, endTime: number): Promise<InspectionResult[]> =>
|
||||
invoke("getResultsByTimeRange", startTime, endTime),
|
||||
};
|
||||
|
||||
// ---------- 告警记录 ----------
|
||||
|
||||
Reference in New Issue
Block a user