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 @@
import{g as os,ap as gs,r as n,j as t,L as ms,q as Y,B as A,aq as us,N as dt,w as bs,o as Ms,x as at,A as Wt,a3 as Hs,D as Kt,b as qt,c as Ut,d as Vt,e as Xt,f as Jt}from"./index-DfyorCky.js";import{a as R}from"./agent-DqqIRCQX.js";import{C,a as S}from"./card-C55cvSHg.js";import{S as Qt,a as Yt,b as Zt,c as ts,d as rt}from"./select-D5qUzdAs.js";import{T as js,a as vs,b as fs,c as G}from"./tabs-CIh_yD4s.js";import{B as Z}from"./badge-xNW8x0G1.js";import{H as W}from"./hard-drive-BbPmnARR.js";import{D as mt}from"./disc-EsE6x5Oh.js";import{C as ut}from"./circle-x-DK79Dhg2.js";import{R as Mt,C as Ht,X as jt,Y as vt,T as ft,q as as}from"./generateCategoricalChart-6v9BPbG2.js";import{A as Nt,a as nt}from"./AreaChart-B2zlWfdX.js";import{R as ss}from"./refresh-cw-DUB_tAG-.js";import{P as Ns}from"./plus-CUG-9kpu.js";import{S as _s}from"./square-pen-BxUFAe0F.js";import{T as ys}from"./trash-2-Dx32pOzT.js";import"./index-CWVNW_qu.js";const ws=[["path",{d:"M2 9.5a5.5 5.5 0 0 1 9.591-3.676.56.56 0 0 0 .818 0A5.49 5.49 0 0 1 22 9.5c0 2.29-1.5 4-3 5.5l-5.492 5.313a2 2 0 0 1-3 .019L5 15c-1.5-1.5-3-3.2-3-5.5",key:"mvr1a0"}],["path",{d:"M3.22 13H9.5l.5-1 2 4.5 2-7 1.5 3.5h5.27",key:"auskq0"}]],ks=os("heart-pulse",ws);const Cs=[["path",{d:"M3 5h.01",key:"18ugdj"}],["path",{d:"M3 12h.01",key:"nlz23k"}],["path",{d:"M3 19h.01",key:"noohij"}],["path",{d:"M8 5h13",key:"1pao27"}],["path",{d:"M8 12h13",key:"1za7za"}],["path",{d:"M8 19h13",key:"m83p4d"}]],es=os("list",Cs),bt=[{label:"1小时",hours:1,window:60,buckets:60},{label:"6小时",hours:6,window:300,buckets:72},{label:"12小时",hours:12,window:600,buckets:72},{label:"1天",hours:24,window:1800,buckets:96},{label:"3天",hours:72,window:3600,buckets:72},{label:"7天",hours:168,window:10800,buckets:56},{label:"14天",hours:336,window:21600,buckets:48}];function u(r,T,O){if(!r||r.length===0)return[];const F=Math.max(1,Math.ceil(r.length/O)),d=[];for(let j=0;j<r.length;j+=F){const y=r.slice(j,j+F),w=T.slice(j,j+F),v=y.reduce((it,K)=>it+K,0)/y.length,f=Math.max(...y),z=Math.min(...y),i=w[Math.floor(w.length/2)]||0;d.push({time:new Date(i*1e3).toLocaleTimeString("zh-CN",{hour:"2-digit",minute:"2-digit"}),timestamp:i,avg:Number(v.toFixed(2)),max:Number(f.toFixed(2)),min:Number(z.toFixed(2)),count:y.length})}return d.slice(-O)}function M(r){if(!r)return{};if(typeof r=="object")return r;try{return JSON.parse(r)}catch{return{}}}function ct(r){return r>=1024*1024*1024?`${(r/1024/1024/1024).toFixed(1)} GB/s`:r>=1024*1024?`${(r/1024/1024).toFixed(1)} MB/s`:r>=1024?`${(r/1024).toFixed(1)} KB/s`:`${r.toFixed(1)} B/s`}function qs(){const[r]=gs(),T=r.get("host")||"",[O,F]=n.useState([]),[d,j]=n.useState(T),[y,w]=n.useState(!0),[P,v]=n.useState("basic"),[f,z]=n.useState(1),[i,it]=n.useState("avg"),[K,_t]=n.useState([]),[o,yt]=n.useState(null),[b,rs]=n.useState(null),[wt,tt]=n.useState([]),[cs,q]=n.useState(!1),[U,kt]=n.useState(null),[a,N]=n.useState({name:"",hostname:"",probe_type:"http_get",http_url:"",http_method:"GET",http_expect_code:200,http_expect_body:"",tcp_host:"",tcp_port:3306,exec_command:"",timeout:10,interval:60,description:""}),V=n.useCallback(()=>{d&&R.getHealthProbes({hostname:d}).then(s=>{Array.isArray(s)?tt(s):s?.list?tt(s.list):tt([])}).catch(()=>tt([]))},[d]),[k,xt]=n.useState("whitelist"),[X,Ct]=n.useState([]),[ds,st]=n.useState(!1),[St,Tt]=n.useState(!1),pt=n.useCallback(()=>{d&&R.getPortPolicy(d).then(s=>{s?.data&&(xt(s.data.port_filter_mode||"blacklist"),Ct(s.data.port_whitelist||[]))}).catch(()=>{})},[d]);n.useEffect(()=>{pt()},[pt]);const ns=async()=>{if(d){Tt(!0);try{await R.savePortPolicy(d,{port_filter_mode:k,port_whitelist:X}),st(!1)}catch{}finally{Tt(!1)}}};n.useEffect(()=>{V()},[V]);const h=bt.find(s=>s.hours===f)||bt[0];n.useEffect(()=>{R.getLatest().then(s=>{const e=Array.isArray(s)?s:s?.data||s?.list||[];if(F(e),e.length>0&&!d){const l=T?e.find(E=>E.hostname===T):null;j(l?.hostname||e[0]?.hostname||"")}const c=e.find(l=>l.hostname===(d||T));c&&(yt(c),rs(c))}).catch(()=>{}).finally(()=>w(!1))},[d]),n.useEffect(()=>{d&&R.getHistory(d,f).then(s=>{const e=Array.isArray(s)?s:s?.data||s?.list||[];if(_t(e),e.length>0){const c=e[e.length-1];yt(l=>({...l,...c}))}}).catch(()=>_t([])).finally(()=>w(!1))},[d,f]);const g=n.useMemo(()=>{const s=[],e=[],c=[],l=[],E=[],et=[],B=[],$t=[],At=[],Ft=[],It=[],Dt=[],Et=[],Bt=[],Ot=[],Rt=[],zt=[],Gt=[],Lt=[],ht=[],m=[];for(const x of K){const is=x.report_time?new Date(x.report_time).getTime()/1e3:x.created_at?new Date(x.created_at).getTime()/1e3:0;m.push(is),s.push(x.cpu_usage||0),Bt.push(x.cpu_iowait||0),e.push(x.mem_usage||0),Ot.push(x.mem_available||0),c.push(x.load1||0),l.push(x.disk_usage||0);const ot=M(x.disk_info);if(Array.isArray(ot)&&ot.length>0){const ls=ot.find(hs=>hs.mount==="/")||ot[0];ht.push(ls?.inode_usage_pct||0)}else ht.push(0);const $=M(x.net_info);E.push($.tcp_established||0),et.push($.tcp_time_wait||0),B.push($.tcp_close_wait||0),zt.push(($.tcp_established||0)+($.tcp_time_wait||0)+($.tcp_close_wait||0)),$t.push(Line truncated