feat(audit): F1+F2 告警静默规则标记完成 + 审计日志CSV导出实现

F1 告警静默规则 [done]:
- 后端 alert_silence.go: CRUD + Toggle (onetime/daily/weekly 三模式)
- 告警引擎 isSilenced 集成 (alert_engine.go)
- 前端 SilenceRuleTab + SilenceDialog
- 路由 /alerts/silences 已注册

F2 审计日志导出 [done]:
- 重写 ExportAuditLogs: 真正的 CSV 导出 (encoding/csv + UTF-8 BOM)
- 支持 module/action/username/startTime/endTime 筛选
- 导出限制 10000 条防溢出,自动记录审计日志
- JSON 格式向后兼容 (format=json)
- 前端 exportLogs 传递查询参数

同时包含累积的前期工作:
- 目录重构 internal/ -> controller/model/router (v0.55-v0.57)
- 后端分层架构 dto/repository/service/utils
- 审计模块全功能 (终端审计/操作审计/登录日志)
- asciicast v2 录制格式
- SFTP 文件浏览器
- StatStrip 紧凑统计条
- 在线会话自动刷新
- LiveMonitor WebSocket 诊断修复

验证: go build ✅ | npm run build ✅ | Docker 部署 ✅ | 10 回归端点 ✅ | CSV 导出 ✅ | 静默 CRUD ✅
This commit is contained in:
attacker committed 2026-06-25 20:16:30 +08:00
1 parent e57718998a
commit 77c234b060
314 files changed
+10050 -4065

No files matched your search

@@ -0,0 +1,2 @@
import{j as t,L,a3 as mt,r as n,D as K,b as U,c as W,d as Y,e as Z,I as $,f as xt,B as p,H as tt,a as pt,S as ht,x as gt,F as ut}from"./index-DfyorCky.js";import{C as w,a as S}from"./card-C55cvSHg.js";import{B as C}from"./badge-xNW8x0G1.js";import{C as B}from"./checkbox-CH0bpj6b.js";import{t as d}from"./index-a3ybMjbR.js";import{s as z,c as y,t as bt}from"./automation-mtL167W_.js";import{C as vt}from"./circle-x-DK79Dhg2.js";import{S as O,a as R,b as A,c as V,d as N}from"./select-D5qUzdAs.js";import{T as Q}from"./textarea-NXY8u32k.js";import{L as J}from"./label-BK0h2apY.js";import{T as yt,a as Ct,b as I,c as E,d as jt,e as f}from"./table-5sHV4Akp.js";import{H as ot}from"./history-D_9jPUQg.js";import{R as Nt}from"./refresh-cw-DUB_tAG-.js";import{P as Jt}from"./plus-CUG-9kpu.js";import{T as q}from"./trash-2-Dx32pOzT.js";import{P as ft}from"./package-open-CUXo2Tda.js";import{P as X}from"./play-cdPayOtJ.js";import{P as Dt}from"./pause-D_famo9P.js";import{S as Mt}from"./square-pen-BxUFAe0F.js";import"./index-CWVNW_qu.js";function et(i){switch(i){case"success":return t.jsxs(C,{"code-path":"src/components/automation/cronJobHelpers.tsx:9:9",variant:"outline",className:"border-emerald-500/30 text-emerald-400 bg-emerald-500/10",children:[t.jsx(mt,{"code-path":"src/components/automation/cronJobHelpers.tsx:10:11",className:"h-3 w-3 mr-1"}),"成功"]});case"failed":return t.jsxs(C,{"code-path":"src/components/automation/cronJobHelpers.tsx:16:9",variant:"outline",className:"border-red-500/30 text-red-400 bg-red-500/10",children:[t.jsx(vt,{"code-path":"src/components/automation/cronJobHelpers.tsx:17:11",className:"h-3 w-3 mr-1"}),"失败"]});case"running":return t.jsxs(C,{"code-path":"src/components/automation/cronJobHelpers.tsx:23:9",variant:"outline",className:"border-blue-500/30 text-blue-400 bg-blue-500/10 animate-pulse",children:[t.jsx(L,{"code-path":"src/components/automation/cronJobHelpers.tsx:24:11",className:"h-3 w-3 mr-1 animate-spin"}),"执行中"]});default:return t.jsx(C,{"code-path":"src/components/automation/cronJobHelpers.tsx:30:9",variant:"outline",className:"border-[var(--dg-border)] text-[var(--dg-text-muted)]",children:"从未执行"})}}function Ft(i){return{"0 2 * * *":"每天 02:00","0 3 * * 0":"每周日 03:00","*/5 * * * *":"每 5 分钟","0 * * * *":"每小时","0 9 * * 1":"每周一 09:00"}[i]||i}const G=[{value:"shell",label:"Shell 脚本",desc:"关联脚本库中的 Shell 脚本"},{value:"python",label:"Python 脚本",desc:"关联脚本库中的 Python 脚本"},{value:"command",label:"自定义命令",desc:"手动输入命令或脚本路径"}];function wt({open:i,onOpenChange:g,editJob:s,onSuccess:D}){const[a,l]=n.useState({name:"",description:"",cron_expr:"",type:"shell",content:"",script_id:null}),[h,v]=n.useState([]),[r,m]=n.useState(null),[H,M]=n.useState(!1);n.useEffect(()=>{i&&(z.getScripts({page_size:200}).then(e=>{v(e?.list||[])}).catch(()=>v([])),s?(l({name:s.name||"",description:s.description||"",cron_expr:s.cron_expr||"",type:s.type||"shell",content:s.content||"",script_id:s.script_id||null}),s.script_id?z.getScript(s.script_id).then(e=>{m(e)}).catch(()=>m(null)):m(null)):(l({name:"",description:"",cron_expr:"",type:"shell",content:"",script_id:null}),m(null)))},[i,s]);const F=a.type==="shell"||a.type==="python"?h.filter(e=>e.type===a.type):[],_=e=>{const x=h.find(j=>String(j.id)===e);x&&(m(x),l(j=>({...j,script_id:x.id,content:x.content})))},P=e=>{l(x=>({...x,type:e,script_id:null,content:e==="command"?x.content:""})),m(null)},k=async()=>{if(!a.name||!a.cron_expr){d.error("请填写任务名称和 Cron 表达式");return}if((a.type==="shell"||a.type==="python")&&!a.script_id){d.error("请选择关联的脚本");return}try{M(!0);const e={name:a.name,description:a.description,cron_expr:a.cron_expr,type:a.type,content:a.content,script_id:a.script_id};s?(await y.updateJob(s.id,e),d.success("任务更新成功")):(await y.createJob(e),d.success("任务创建成功")),g(!1),D()}catch(e){console.error("Failed to save cron job:",e),d.error(s?"更新失败":"创建失败")}finally{M(!1)}};return t.jsx(K,{"code-path":"src/components/automation/CronJobFormDialog.tsx:149:5",open:i,onOpenChange:g,children:t.jsxs(U,{"code-path":"src/components/automation/CronJobFormDialog.tsx:150:7",className:"max-w-2xl bg-[var(--dg-bg-primary)] border-[var(--dg-border)] text-[var(--dg-text-primary)]",children:[t.jsxs(W,{"code-path":"src/components/automation/CronJobFormDialog.tsx:151:9",children:[t.jsx(Y,{"code-path":"src/components/automation/CronJobFormDialog.tsx:152:11",children:s?"编辑定时任务":"新建定时任务"}),t.jsx(Z,{"code-path":"src/components/automation/CronJobFormDialog.tsx:153:11",className:"text-[var(--dg-text-secondary)]",children:"配置 Cron 定时任务"})]}),t.jsxs("div",{"code-path":"src/components/automation/CronJobFormDialog.tsx:155:9",className:"space-y-4 py-4 max-h-[60vh] overflow-y-auto pr-1",children:[t.jsxs("div",{"code-path":"src/components/automation/CronJobFormDialog.tsx:1Line truncated
`).length," 行"]})]}),t.jsx("pre",{"code-path":"src/components/automation/CronJobFormDialog.tsx:275:19",className:"max-h-48 overflow-auto rounded-md bg-[var(--dg-bg-tertiary)] border border-[var(--dg-border)] p-3 text-xs font-mono text-[var(--dg-text-secondary)] whitespace-pre-wrap break-all",children:r.content||"(空脚本)"})]})]}):t.jsxs("div",{"code-path":"src/components/automation/CronJobFormDialog.tsx:282:13",children:[t.jsx(J,{"code-path":"src/components/automation/CronJobFormDialog.tsx:283:15",className:"text-[var(--dg-text-primary)]",children:"执行命令"}),t.jsx(Q,{"code-path":"src/components/automation/CronJobFormDialog.tsx:284:15",value:a.content,onChange:e=>l({...a,content:e.target.value}),placeholder:"/scripts/backup.sh arg1 arg2",rows:4,className:"bg-[var(--dg-bg-secondary)] border-[var(--dg-border)] text-[var(--dg-text-primary)] mt-1 font-mono"})]})]}),t.jsxs(xt,{"code-path":"src/components/automation/CronJobFormDialog.tsx:294:9",children:[t.jsx(p,{"code-path":"src/components/automation/CronJobFormDialog.tsx:295:11",variant:"outline",onClick:()=>g(!1),className:"border-[var(--dg-border)] text-[var(--dg-text-secondary)]",children:"取消"}),t.jsx(p,{"code-path":"src/components/automation/CronJobFormDialog.tsx:298:11",className:"bg-blue-600 hover:bg-blue-700",onClick:k,disabled:H,children:H?"保存中...":s?"保存修改":"创建任务"})]})]})})}function St({open:i,onOpenChange:g}){const[s,D]=n.useState([]),[a,l]=n.useState(!1),[h,v]=n.useState(null);return n.useEffect(()=>{if(!i)return;(async()=>{try{l(!0);const m=await bt.getExecutions({task_type:"cron",page_size:50});D(m.list||[])}catch(m){console.error("Failed to load executions:",m)}finally{l(!1)}})()},[i]),t.jsx(K,{"code-path":"src/components/automation/ExecutionHistoryDialog.tsx:51:5",open:i,onOpenChange:g,children:t.jsxs(U,{"code-path":"src/components/automation/ExecutionHistoryDialog.tsx:52:7",className:"max-w-4xl bg-[var(--dg-bg-primary)] border-[var(--dg-border)] text-[var(--dg-text-primary)] max-h-[85vh] overflow-y-auto",children:[t.jsxs(W,{"code-path":"src/components/automation/ExecutionHistoryDialog.tsx:53:9",children:[t.jsx(Y,{"code-path":"src/components/automation/ExecutionHistoryDialog.tsx:54:11",children:"执行历史"}),t.jsx(Z,{"code-path":"src/components/automation/ExecutionHistoryDialog.tsx:55:11",className:"text-[var(--dg-text-secondary)]",children:"定时任务执行记录与输出日志"})]}),t.jsx("div",{"code-path":"src/components/automation/ExecutionHistoryDialog.tsx:57:9",className:"mt-4",children:a?t.jsx("div",{"code-path":"src/components/automation/ExecutionHistoryDialog.tsx:59:13",className:"flex items-center justify-center py-8",children:t.jsx(L,{"code-path":"src/components/automation/ExecutionHistoryDialog.tsx:60:15",className:"h-6 w-6 text-[var(--dg-text-secondary)] animate-spin"})}):s.length===0?t.jsxs("div",{"code-path":"src/components/automation/ExecutionHistoryDialog.tsx:63:13",className:"flex flex-col items-center justify-center py-12 text-[var(--dg-text-secondary)]",children:[t.jsx(ot,{"code-path":"src/components/automation/ExecutionHistoryDialog.tsx:64:15",className:"h-10 w-10 mb-3 opacity-40"}),t.jsx("p",{"code-path":"src/components/automation/ExecutionHistoryDialog.tsx:65:15",className:"text-sm",children:"暂无执行记录"})]}):t.jsxs(yt,{"code-path":"src/components/automation/ExecutionHistoryDialog.tsx:68:13",children:[t.jsx(Ct,{"code-path":"src/components/automation/ExecutionHistoryDialog.tsx:69:15",children:t.jsxs(I,{"code-path":"src/components/automation/ExecutionHistoryDialog.tsx:70:17",className:"border-[var(--dg-border)]",children:[t.jsx(E,{"code-path":"src/components/automation/ExecutionHistoryDialog.tsx:71:19",className:"text-[var(--dg-text-secondary)]",children:"任务"}),t.jsx(E,{"code-path":"src/components/automation/ExecutionHistoryDialog.tsx:72:19",className:"text-[var(--dg-text-secondary)]",children:"状态"}),t.jsx(E,{"code-path":"src/components/automation/ExecutionHistoryDialog.tsx:73:19",className:"text-[var(--dg-text-secondary)]",children:"耗时"}),t.jsx(E,{"code-path":"src/components/automation/ExecutionHistoryDialog.tsx:74:19",className:"text-[var(--dg-text-secondary)]",children:"执行时间"}),t.jsx(E,{"code-path":"src/components/automation/ExecutionHistoryDialog.tsx:75:19",className:"text-[var(--dg-text-secondary)]",children:"输出"})]})}),t.jsx(jt,{"code-path":"src/components/automation/ExecutionHistoryDialog.tsx:78:15",children:s.map(r=>t.jsxs(n.Fragment,{"code-path":"src/components/automation/ExecutionHistoryDialog.tsx:80:19",children:[t.jsxs(I,{"code-path":"src/components/automation/ExecutionHistoryDialog.tsx:81:21",className:"border-[var(--dg-border)] cursor-pointer hover:bg-[var(--dg-bg-hover)]/50",onClick:()=>v(h===r.id?null:r.id),children:[t.jsx(f,{"code-path":"src/components/automation/ExecutionHistoryDialog.tsx:82:23",className:"text-[var(--dg-text-primary)]",children:r.task_name}),t.jsx(f,{"code-path":"src/components/automation/ExecutionHistoryDialog.tsx:83 Line truncated