/** * 巡检模块 - 前端 API 层 * * 通过 window.electronAPI.inspection 调用主进程的巡检 IPC handlers。 */ // ---------- 类型定义 ---------- export type AlertLevel = "critical" | "warning" | "info"; export type DeviceTier = "core" | "aggregation" | "access"; export type InspectionStatus = "success" | "failed" | "timeout"; export type AlertRecordStatus = "active" | "acknowledged" | "resolved" | "closed"; export type Operator = ">" | "<" | "==" | "!=" | ">=" | "<="; export interface InspectionLabels { alert_levels: Record; device_tiers: Record; inspection_status: Record; alert_record_status: Record; operators: Record; device_catalog: Record; metrics: Record; } export interface DeviceCatalog { id: string; type_key: string; name: string; vendor: string; adapter_class: string; supported_metrics: string[]; default_thresholds: AlertRule[]; default_interval: number; description: string; preset: boolean; } export interface DeviceTierInfo { key: DeviceTier; label: string; default_interval: number; } export interface DeviceExtension { host_id: string; /** 主机名(IP 或域名),用于调度器匹配 vault SSH 配置 */ hostname?: string | null; catalog_id: string; tier: DeviceTier | null; inspection_interval: number | null; inspection_enabled: boolean; custom_thresholds: AlertRule[]; inspection_tags: string[]; last_scan_at: number | null; last_scan_status: InspectionStatus | null; last_scan_error: string | null; created_at: number; updated_at: number; effective_interval?: number; } export interface AlertRule { id: string; name: string; metric: string; operator: Operator; threshold: string; level: AlertLevel; duration: number; scope_type: "all" | "tier" | "device" | "catalog"; scope_value: string | string[] | null; enabled: boolean; preset?: boolean; created_at: number; updated_at: number; } export interface InspectionResult { id: string; job_id: string | null; device_id: string; timestamp: number; online: boolean; status: InspectionStatus; duration_ms: number; metrics: Record; alerts: any[]; error_message: string | null; } export interface AlertRecord { id: string; rule_id: string; rule_name: string; device_id: string; device_name: string; metric: string; metric_value: string; threshold: string; level: AlertLevel; message: string; triggered_at: number; status: AlertRecordStatus; acknowledged_by: string | null; acknowledged_at: number | null; resolved_at: number | null; resolved_reason: string | null; note: string | null; // v1.3 预案管理扩展字段 alert_type?: string; alert_catalog?: string; matched_plan_id?: string | null; matched_template_id?: string | null; matched_plan_snapshot?: DevicePlan | null; // D9 匹配时刻冻结 frozen_at?: number | null; initial_response?: InitialResponse | null; remediation?: RemediationRun | null; escalation?: { trigger: "aggregation" | "escalation_fallback_timeout"; source_alert_ids: string[]; target_level: string; escalated_at: number; } | null; audit_log?: Array<{ ts: number; actor: string; action: string; detail?: string | null; }>; } // ---------- v1.3 预案管理类型 ---------- export interface DevicePlan { id: string; template_id: string | null; device_type: string; level: AlertLevel; enabled: boolean; builtin: boolean; status: "active" | "draft" | "disabled"; source: "manual" | "ai"; ai_provider: string | null; ai_prompt_version: string | null; reject_reason: string | null; review_log: Array<{ ts: number; actor: string; action: "approve" | "reject" | "ai_generate"; reason: string | null }>; description: string; match: { metric_rules: Array<{ metric_key: string; op: string; value: string | number }>; alert_types: string[]; device_tiers: string[]; device_tags: string[]; }; scope: { host_ids: string[]; host_groups: string[] }; human_action_guide: { receiving_alerts: string[]; info_gathering: Array<{ priority: number; action: string; purpose: string }>; notification_chain: Array<{ role: string; channel: string; trigger: string; contact: string; auto: boolean }>; escalation_triggers: Array<{ condition: string; target_level: string }>; pre_remediation: string[]; post_remediation: string[]; }; remediation: { steps: Array<{ order: number; action: "command" | "checklist"; command_id?: string; command_params?: Record; timeout_sec?: number; execution: "auto" | "confirm" | "manual_only"; on_success: "next" | "stop" | "skip"; on_failure: "stop" | "escalate" | string; purpose?: string; }>; }; security: { allowed_command_ids: string[]; allowed_commands: string[]; blocked_patterns: string[]; }; required_role: string; escalation_fallback: { auto_escalate_minutes: number; escalation_target_level: string } | null; created_at: number; updated_at: number; } export interface InitialResponse { alert_id: string; generated_at: number; generator: "ai" | "fallback"; status: string; quick_checklist: Array<{ priority: number; action: string; purpose: string }>; possible_causes?: Array<{ probability: string; label: string; hint: string }>; matched_plan: { name: string; level: string }; auto_notifications_sent: Array<{ role: string; channel: string; status: string }>; pending_notifications: Array>; warnings: string[]; } export interface RemediationRun { status: "pending" | "running" | "completed" | "partial" | "failed" | "cancelled" | "paused"; started_at: number; completed_at?: number; steps: Array<{ order: number; status: "pending" | "running" | "success" | "failed" | "skipped" | "pending_manual"; command_id?: string; execution: "auto" | "confirm" | "manual_only"; stdout_preview?: string | null; exit_code?: number | null; error?: string | null; confirmed_by?: string | null; confirmed_at?: number | null; started_at?: number | null; finished_at?: number | null; }>; } export interface AlertStats { total: number; active: number; active_critical: number; active_warning: number; active_info: number; acknowledged: number; resolved: number; closed: number; } export interface SchedulerStatus { running: boolean; queue_size: number; active_scans: number; concurrency: number; } // ---------- API 调用 ---------- const api = (window as any).netcatty?.inspection; // 浏览器远程访问环境:通过 HTTP API fallback const IS_REMOTE_BROWSER = typeof window !== "undefined" && !!(window as any).__REMOTE_TOKEN__; const REMOTE_TOKEN = (window as any).__REMOTE_TOKEN__ || ""; const REMOTE_API_MAP: Record = { 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", "getResultsByTimeRange", "getActiveAlerts", "listAlerts", "getAllExtensions", "getEnabledDevices", "getCatalog", ]); async function remoteFetch(name: string, ...args: any[]): Promise { const mapping = REMOTE_API_MAP[name]; if (!mapping) { // 没有映射的方法:读场景返回空数组/空对象兜底,避免 .find 崩溃 console.warn(`[Inspection] remote fallback: no mapping for ${name}, returning safe default`); if (ARRAY_METHODS.has(name)) return []; if (name === "getSchedule" || name === "getReportSchedule") return {}; if (name === "getSysConfig") return {}; if (name === "getLabels") return { alert_levels: {}, device_tiers: {}, inspection_status: {}, alert_record_status: {}, operators: {}, device_catalog: {}, metrics: {} }; if (name === "schedulerStatus" || name === "isReportSnapshotRunning") return { running: false }; return null; } 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}`; if (opts.level) url += `&level=${opts.level}`; if (opts.device_id) url += `&device_id=${opts.device_id}`; if (opts.limit) url += `&limit=${opts.limit}`; } else if (args[0] !== undefined && name !== "getRule") { url += `&limit=${encodeURIComponent(args[0])}`; } try { const fetchOpts: RequestInit = {}; if (mapping.method === "POST") { fetchOpts.method = "POST"; fetchOpts.headers = { "Content-Type": "application/json" }; if (name === "batchSetInspectionEnabled") { fetchOpts.body = JSON.stringify({ enabled: !!args[0] }); } else if (name === "runReportAiSummary") { // invoke("runReportAiSummary", snapshotId, providerId) fetchOpts.body = JSON.stringify({ snapshotId: args[0], providerId: args[1] }); } 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] : {}); } } const res = await fetch(url, fetchOpts); const data = await res.json(); // 适配不同返回结构 if (data && typeof data === "object") { if (Array.isArray(data)) return data; // getAllExtensions:必须把 devices 数组转成 Record if (name === "getAllExtensions" && Array.isArray(data.devices)) { const map: Record = {}; 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) 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 && (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; } return null; } catch (e) { console.warn(`[Inspection] remote fetch ${name} failed:`, e); // fetch 失败也返回安全默认值,不要让 .find 崩 if (ARRAY_METHODS.has(name)) return []; return null; } } function isAvailable(): boolean { return !!api || IS_REMOTE_BROWSER; } function invoke(name: string, ...args: any[]): Promise { if (api && typeof api[name] === "function") { return api[name](...args); } // Electron bridge 不可用 → 浏览器远程访问环境走 HTTP fallback if (IS_REMOTE_BROWSER) { return remoteFetch(name, ...args); } console.warn(`[Inspection] api.${name} 不可用`); return Promise.resolve(null); } // ---------- 标签 ---------- export const inspectionLabelsApi = { isAvailable, getLabels: (): Promise => invoke("getLabels"), }; // ---------- 系统配置 ---------- export const inspectionSysConfigApi = { get: (): Promise> => invoke("getSysConfig"), update: (updates: Record) => invoke("updateSysConfig", updates), }; // ---------- 设备类型 ---------- export const inspectionCatalogApi = { list: (): Promise => invoke("getCatalog"), getTiers: (): Promise => invoke("getTiers"), getAdapters: (): Promise => invoke("getAdapters"), }; // ---------- 设备扩展 ---------- export const inspectionDeviceApi = { getAll: (): Promise> => invoke("getAllExtensions"), get: (hostId: string): Promise => invoke("getExtension", hostId), upsert: (hostId: string, data: Partial): Promise => invoke("upsertExtension", hostId, data), batchSetEnabled: (enabled: boolean): Promise<{ ok: boolean; count: number; enabled: boolean; error?: string }> => invoke("batchSetInspectionEnabled", enabled), remove: (hostId: string): Promise => invoke("deleteExtension", hostId), getEffectiveInterval: (hostId: string): Promise => invoke("getEffectiveInterval", hostId), getEnabled: (): Promise => invoke("getEnabledDevices"), }; // ---------- 告警规则 ---------- export const inspectionAlertRuleApi = { list: (): Promise => invoke("listRules"), get: (ruleId: string): Promise => invoke("getRule", ruleId), create: (data: Partial): Promise => invoke("createRule", data), update: (ruleId: string, updates: Partial): Promise => invoke("updateRule", ruleId, updates), remove: (ruleId: string): Promise => invoke("deleteRule", ruleId), toggle: (ruleId: string, enabled: boolean): Promise => invoke("toggleRule", ruleId, enabled), }; // ---------- 巡检结果 ---------- export const inspectionResultApi = { getLatestAll: (): Promise => invoke("getLatestAll"), getLatestByDevice: (hostId: string, limit = 20): Promise => invoke("getLatestByDevice", hostId, limit), getRecent: (limit = 50): Promise => invoke("getRecentResults", limit), getByTimeRange: (startTime: number, endTime: number): Promise => invoke("getResultsByTimeRange", startTime, endTime), }; // ---------- 告警记录 ---------- export const inspectionAlertApi = { list: (options?: { status?: AlertRecordStatus; level?: AlertLevel; device_id?: string; limit?: number; }): Promise => invoke("listAlerts", options), getActive: (): Promise => invoke("getActiveAlerts"), getStats: (): Promise => invoke("getAlertStats"), acknowledge: (alertId: string, operator = "system"): Promise => invoke("acknowledgeAlert", alertId, operator), resolve: ( alertId: string, reason = "manual", operator = "system", ): Promise => invoke("resolveAlert", alertId, reason, operator), close: (alertId: string, note = ""): Promise => invoke("closeAlert", alertId, note), updateNote: (alertId: string, note: string): Promise => invoke("updateAlertNote", alertId, note), }; // ---------- 调度器 ---------- // SSHConfig 是用于 SSH 连接的主机配置(hostname/port/username/password/privateKey 等) export type SshHostConfig = Record; export const inspectionSchedulerApi = { start: (hostConfigs?: Record): Promise => invoke("schedulerStart", hostConfigs ? { hostConfigs } : {}), stop: (): Promise => invoke("schedulerStop"), status: (): Promise => invoke("schedulerStatus"), scanNow: (hostId: string): Promise => invoke("scanNow", hostId), scanAllNow: (): Promise => invoke("scanAllNow"), getAutoEnabled: (): Promise => invoke("getAutoEnabled"), setAutoEnabled: (enabled: boolean): Promise<{ enabled: boolean }> => invoke("setAutoEnabled", enabled), }; // ---------- 单次巡检执行 ---------- export interface RunInspectionResult { success: boolean; status: string; metrics: Record; error: string | null; duration: number; alerts: any[]; } export interface HostConfigForInspection { id: string; label?: string; hostname: string; port?: number; username: string; password?: string; privateKeyContent?: string; passphrase?: string; } export const inspectionExecApi = { /** * 执行一次完整巡检(前端传入完整主机配置) * @param hostId 主机 ID * @param hostConfig 主机连接配置 * @param catalogId 设备类型 ID(采集器 ID) */ runInspection: ( hostId: string, hostConfig: HostConfigForInspection, catalogId: string ): Promise => invoke("runInspection", { hostId, hostConfig, catalogId }), }; // =============== 巡检报告快照 =============== export interface ReportCmdResult { cmd: string; purpose: string; ok: boolean; duration_ms: number; stdout: string; stderr: string; } export interface ReportDeviceResult { device_id: string; hostname: string; catalog_id: string; ok: boolean; duration_ms: number; commands: ReportCmdResult[]; } export interface ReportSnapshot { id: string; timestamp: number; duration_ms: number; trigger: "manual" | "scheduled"; devices: ReportDeviceResult[]; /** AI 汇总结果(主进程自动生成后持久化到 JSON) */ ai_summary?: any; /** AI 汇总生成状态:running / done / failed */ ai_summary_status?: "running" | "done" | "failed"; /** AI 汇总失败时的错误信息 */ ai_summary_error?: string; /** AI 汇总完成时间戳 */ ai_summary_updated_at?: number; } export interface ReportDeviceTarget { device_id: string; hostname: string; port?: number; username: string; password?: string; privateKeyContent?: string; passphrase?: string; catalog_id?: string; } export interface RunReportSnapshotResult { ok: boolean; snapshot?: ReportSnapshot; error?: string; } /** 从 renderer localStorage 读报告产品名(优先报告专用 key,fallback 通用产品名) * 注意:useStoredString 的 writeString 直接存纯字符串,不 JSON.stringify, * 所以这里不能 JSON.parse,直接判断值类型即可。 */ function _getReportSystemName(): string | undefined { try { const reportRaw = localStorage.getItem("netcatty_report_product_name_v1"); if (typeof reportRaw === "string" && reportRaw.trim()) return reportRaw.trim(); const productRaw = localStorage.getItem("netcatty_product_name_v1"); if (typeof productRaw === "string" && productRaw.trim()) return productRaw.trim(); } catch (_) {} return undefined; } export const inspectionReportApi = { /** * 跑一次巡检报告快照 —— 所有 deviceTargets 独立 SSH 连设备执行各自 catalog 的命令组 */ runSnapshot: ( deviceTargets: ReportDeviceTarget[], trigger: "manual" | "scheduled" = "manual" ): Promise => invoke("runReportSnapshot", { deviceTargets, trigger }), /** 查询主进程当前是否有快照正在执行(页面切换后查状态用) */ isSnapshotRunning: (): Promise<{ running: boolean }> => invoke("isReportSnapshotRunning"), /** * 列出最近 N 条快照(新的在前) */ listSnapshots: (limit = 50): Promise => invoke("listReportSnapshots", limit), /** * 删除一条快照 */ deleteSnapshot: (id: string): Promise => invoke("deleteReportSnapshot", id), /** * 清空所有快照 */ clearSnapshots: (): Promise<{ ok: boolean }> => invoke("clearReportSnapshots"), /** * 导出快照 PDF * @param options.snapshotId 快照 ID * @param options.aiSummary 可选 AI 汇总(有则 PDF 含 AI 分析) * @param options.customPath 可选用户指定保存路径(下载按钮用) */ exportSnapshotPdf: (options: { snapshotId: string; aiSummary?: any; customPath?: string; }): Promise<{ ok: boolean; filePath?: string; error?: string }> => invoke("exportReportSnapshotPdf", { ...options, systemName: _getReportSystemName() }), /** 内嵌预览(Electron BrowserWindow + 内置 PDF viewer) */ previewSnapshotPdf: (snapshotId: string): Promise<{ ok: boolean; filePath?: string; error?: string }> => invoke("previewReportSnapshotPdf", { snapshotId, systemName: _getReportSystemName() }), /** 保存 AI 汇总到快照(持久化,切换页面不丢) */ saveAiSummary: (snapshotId: string, aiSummary: any): Promise<{ ok: boolean }> => invoke("saveReportAiSummary", { snapshotId, aiSummary }), /** 清空快照的 AI 汇总(用于重试前) */ clearAiSummary: (snapshotId: string): Promise<{ ok: boolean }> => invoke("clearReportAiSummary", { snapshotId }), /** 主进程执行 AI 汇总 —— 独立进程,页面切换不影响 */ runAiSummary: (snapshotId: string, providerId?: string): Promise<{ ok: boolean }> => invoke("runReportAiSummary", snapshotId, providerId), }; // =============== 巡检报告调度配置 =============== export interface ReportSchedule { enabled: boolean; defaultIntervalHours: number; dailyReportTime: string; // "HH:MM" enabledDeviceIds: string[] | null; // null=全部;[]=一个都不选;[id1]=指定 perDeviceInterval: Record; aiSummaryEnabled: boolean; } export const inspectionReportScheduleApi = { get: (): Promise => invoke("getReportSchedule"), save: (config: Partial): Promise => invoke("saveReportSchedule", config), toggleScheduler: (enabled: boolean): Promise<{ ok: boolean; running: boolean }> => invoke("toggleReportScheduler", enabled), status: (): Promise<{ running: boolean; schedule: ReportSchedule }> => invoke("reportSchedulerStatus"), }; // =============== 日运行报告 =============== export interface DailyReportSummary { snapshotCount: number; deviceCount: number; totalCmds: number; okCmds: number; failCmds: number; failRate: number; deviceSuccessRate: Record; topFailedCommands: Array<{ device_id: string; cmd: string; count: number }>; } export interface DailyReport { id: string; date: string; // "YYYY-MM-DD" mode: "provisional" | "finalized"; timestamp: number; trigger: "manual" | "scheduled"; period: { start_ts: number; end_ts: number }; summary: DailyReportSummary; aiSummary: any | null; } export const inspectionDailyReportApi = { list: (limit = 30): Promise => invoke("listDailyReports", limit), generate: (options?: { mode?: "provisional" | "finalized"; trigger?: "manual" | "scheduled"; }): Promise<{ ok: boolean; report?: DailyReport; error?: string }> => invoke("generateDailyReport", options || {}), delete: (id: string): Promise => invoke("deleteDailyReport", id), clear: (): Promise<{ ok: boolean }> => invoke("clearDailyReports"), exportPdf: (reportId: string): Promise<{ ok: boolean; filePath?: string; error?: string }> => invoke("exportDailyReportPdf", { reportId, systemName: _getReportSystemName() }), /** 内嵌预览(Electron BrowserWindow + 内置 PDF viewer) */ previewPdf: (reportId: string): Promise<{ ok: boolean; filePath?: string; error?: string }> => invoke("previewDailyReportPdf", { reportId, systemName: _getReportSystemName() }), }; export const inspectionSnapshotExportApi = { exportPdf: (snapshotId: string, aiSummary?: any): Promise<{ ok: boolean; filePath?: string; error?: string }> => invoke("exportReportSnapshotPdf", aiSummary ? { snapshotId, aiSummary } : snapshotId), }; // =============== v1.3 预案管理 =============== export interface PlanTemplate { id: string; name: string; level: AlertLevel; builtin: boolean; description: string; display: { color: string; icon: string; blink: boolean; sound: string | null }; notification: { channels: string[]; recipients: any[]; repeat_interval_minutes: number; max_repeats: number }; escalation_fallback: { auto_escalate_minutes: number; escalation_target_level: string } | null; remediation_defaults: { require_confirmation: boolean; auto_close_after_remediation_recovery: boolean; execution_timeout_sec: number }; created_at: number; updated_at: number; } export interface CommandCatalogItem { id: string; name: string; command: string; device_types: string[]; risk_level: "low" | "medium" | "high"; execution: "auto" | "confirm" | "manual_only"; category: "diagnostic" | "config" | "security" | "backup" | "recovery"; description: string; builtin: boolean; created_at: number; updated_at: number; } export const planManagementApi = { // 预案模板 listTemplates: (): Promise => invoke("listPlanTemplates"), getTemplate: (id: string): Promise => invoke("getPlanTemplate", id), createTemplate: (data: Partial): Promise => invoke("createPlanTemplate", data), updateTemplate: (id: string, updates: Partial): Promise => invoke("updatePlanTemplate", id, updates), deleteTemplate: (id: string): Promise => invoke("deletePlanTemplate", id), // 设备预案 listPlans: (filters?: { status?: string; device_type?: string; level?: string; enabled?: boolean }): Promise => invoke("listDevicePlans", filters || {}), getPlan: (id: string): Promise => invoke("getDevicePlan", id), createPlan: (data: Partial): Promise => invoke("createDevicePlan", data), updatePlan: (id: string, updates: Partial): Promise => invoke("updateDevicePlan", id, updates), deletePlan: (id: string): Promise => invoke("deleteDevicePlan", id), approvePlan: (id: string, actor = "admin"): Promise => invoke("approveDevicePlan", id, actor), rejectPlan: (id: string, reason: string, actor = "admin"): Promise => invoke("rejectDevicePlan", id, reason, actor), matchPlan: (alert: any, deviceProfile: any): Promise<{ plan: DevicePlan; score: number } | null> => invoke("matchPlanForAlert", alert, deviceProfile), // 聚合规则 listAggregationRules: (): Promise => invoke("listAggregationRules"), createAggregationRule: (data: any): Promise => invoke("createAggregationRule", data), updateAggregationRule: (id: string, updates: any): Promise => invoke("updateAggregationRule", id, updates), deleteAggregationRule: (id: string): Promise => invoke("deleteAggregationRule", id), // 命令目录 listCommandCatalog: (filters?: { device_type?: string; execution?: string; risk_level?: string }): Promise => invoke("listCommandCatalog", filters || {}), getCommandCatalog: (id: string): Promise => invoke("getCommandCatalog", id), // AI 预案生成 generatePlan: (description: string): Promise<{ ok: boolean; plan?: DevicePlan; warnings?: string[]; validation?: any; error?: string }> => invoke("generatePlan", description), // 初始响应包 regenerateInitialResponse: (alertId: string): Promise<{ ok: boolean; response?: any; error?: string }> => invoke("regenerateInitialResponse", alertId), // 处置执行 startRemediation: (alertId: string): Promise<{ ok: boolean; result?: any; error?: string }> => invoke("startRemediation", alertId), pauseRemediation: (alertId: string): Promise<{ ok: boolean }> => invoke("pauseRemediation", alertId), resumeRemediation: (alertId: string): Promise<{ ok: boolean }> => invoke("resumeRemediation", alertId), cancelRemediation: (alertId: string): Promise<{ ok: boolean }> => invoke("cancelRemediation", alertId), getRemediationStatus: (alertId: string): Promise => invoke("getRemediationStatus", alertId), // AlertStateMonitor getAlertStateMonitorStatus: (): Promise<{ running: boolean; interval_minutes: number; remediation_timeout_minutes: number }> => invoke("getAlertStateMonitorStatus"), };