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,3 @@
import{g as Le,r as a,j as e,D as ie,b as le,c as oe,d as ne,e as de,L as pe,W as we,aq as Q,a8 as Oe,q as Se,x as Ce,a as xe,A as he,S as I,X as _e,I as E,B as y,U as me,a5 as ue,aQ as ke,O as De,z as Me}from"./index-DfyorCky.js";import{C as P,a as K}from"./card-C55cvSHg.js";import{B as w}from"./badge-xNW8x0G1.js";import{T as H,a as W,b as M,c as i,d as U,e as l}from"./table-5sHV4Akp.js";import{t as F,s as Re,a as X,l as ze}from"./system-BgtMMm0X.js";import{x as ge,a as be}from"./xterm-DSqgeKvr.js";import{R as q}from"./refresh-cw-DUB_tAG-.js";import{P as Be}from"./play-cdPayOtJ.js";import{D as Te}from"./download-DoZfYs2h.js";const Ie=[["path",{d:"m15 9-6 6",key:"1uzhvr"}],["path",{d:"M2.586 16.726A2 2 0 0 1 2 15.312V8.688a2 2 0 0 1 .586-1.414l4.688-4.688A2 2 0 0 1 8.688 2h6.624a2 2 0 0 1 1.414.586l4.688 4.688A2 2 0 0 1 22 8.688v6.624a2 2 0 0 1-.586 1.414l-4.688 4.688a2 2 0 0 1-1.414.586H8.688a2 2 0 0 1-1.414-.586z",key:"2d38gg"}],["path",{d:"m9 9 6 6",key:"z0biqf"}]],$e=Le("octagon-x",Ie);function Ee({open:s,onOpenChange:o,sessionId:d}){const n=a.useRef(null),p=a.useRef(null),T=a.useRef(null),[h,L]=a.useState(!1),[b,O]=a.useState(""),[j,v]=a.useState(0);return a.useEffect(()=>{if(!s||!d)return;const r=new ge.Terminal({cursorBlink:!1,fontSize:13,fontFamily:'Menlo, Monaco, "Courier New", monospace',disableStdin:!0,cols:80,rows:24,theme:{background:"#0a0e14",foreground:"#d6d6d6",cursor:"#0a0e14",selectionBackground:"rgba(255,255,255,0.2)"}}),m=new be.FitAddon;r.loadAddon(m),p.current=r;let f=!1;return L(!0),O(""),v(0),F.getRecord(d).then(u=>{if(f)return;const C=typeof u=="string"?u:u?.data||u?.record_data||"";if(!C||C.trim()===""){O("No recording data available for this session"),L(!1);return}if(n.current){r.open(n.current);try{m.fit()}catch{}}const R=C.split(`
`).filter(x=>x.trim()),S=[];for(const x of R)try{const g=JSON.parse(x);Array.isArray(g)&&g.length>=3&&S.push({time:g[0],eventType:g[1],data:g[2]})}catch{}if(S.length===0){r.write(C),v(100),L(!1);return}L(!1);const $=S[S.length-1].time;let N=0,z=0;const _=()=>{if(f||N>=S.length){v(100);return}const x=S[N],g=(x.time-z)*1e3;z=x.time,N++;const B=Math.min(g,2e3);T.current=setTimeout(()=>{f||(r.write(x.data),v(Math.round(x.time/$*100)),_())},B)};_()}).catch(u=>{f||(console.error("[Replay] Load failed:",u),O("Failed to load recording data"),L(!1))}),()=>{f=!0,T.current&&clearTimeout(T.current);try{r.dispose()}catch{}p.current=null}},[s,d]),e.jsx(ie,{"code-path":"src/components/system/ReplayDialog.tsx:153:5",open:s,onOpenChange:o,children:e.jsxs(le,{"code-path":"src/components/system/ReplayDialog.tsx:154:7",showOverlay:!1,className:"bg-slate-900 border-slate-800 p-0 gap-0",style:{maxWidth:"900px",width:"90vw"},children:[e.jsxs(oe,{"code-path":"src/components/system/ReplayDialog.tsx:155:9",className:"px-4 py-3 border-b border-slate-800",children:[e.jsxs(ne,{"code-path":"src/components/system/ReplayDialog.tsx:156:11",className:"text-slate-100 text-base flex items-center gap-2",children:["Terminal Replay #",d??"",j>0&&j<100&&e.jsxs("span",{"code-path":"src/components/system/ReplayDialog.tsx:159:15",className:"text-xs text-slate-500 font-normal ml-2",children:[j,"%"]})]}),e.jsx(de,{"code-path":"src/components/system/ReplayDialog.tsx:162:11",className:"text-slate-500 sr-only",children:"Replay recorded terminal output"})]}),e.jsxs("div",{"code-path":"src/components/system/ReplayDialog.tsx:164:9",className:"relative bg-[#0a0e14]",style:{height:"460px"},children:[h&&e.jsx("div",{"code-path":"src/components/system/ReplayDialog.tsx:166:13",className:"absolute inset-0 z-10 flex items-center justify-center bg-black/40",children:e.jsx(pe,{"code-path":"src/components/system/ReplayDialog.tsx:167:15",className:"h-6 w-6 animate-spin text-blue-400"})}),b&&e.jsx("div",{"code-path":"src/components/system/ReplayDialog.tsx:171:13",className:"absolute inset-0 z-10 flex items-center justify-center bg-black/40",children:e.jsx("p",{"code-path":"src/components/system/ReplayDialog.tsx:172:15",className:"text-red-400 text-sm",children:b})}),j>0&&j<100&&e.jsx("div",{"code-path":"src/components/system/ReplayDialog.tsx:176:13",className:"absolute bottom-0 left-0 right-0 h-0.5 bg-slate-800 z-10",children:e.jsx("div",{"code-path":"src/components/system/ReplayDialog.tsx:177:15",className:"h-full bg-blue-500 transition-all duration-300",style:{width:`${j}%`}})}),e.jsx("div",{"code-path":"src/components/system/ReplayDialog.tsx:183:11",ref:n,className:"absolute inset-0 p-1"})]})]})})}function Fe({open:s,onOpenChange:o,sessionId:d,sessionInfo:n}){const p=a.useRef(null),T=a.useRef(null),h=a.useRef(null),[L,b]=a.useState(!1),[O,j]=a.useState(""),[v,r]=a.useState(n||null);a.useEffect(()=>{if(!s||!d)return;const f=p.current;if(!f)return;const u=new ge.Terminal({cursorBlink:!1,fontSize:13,fontFamily:'Menlo, Monaco, "Courier New", monospace',disableStdin:!0,theme:{background:"#0a0e14",foreground:"#d6d6d6",cursor:"#d6d6d6",selectionBackground:"#264f78",black:"#1a1a2e",red:"#ff6b6b",green:"#69db7c",yellow:"#ffd43b",blue:"#4dabf7",magenta:"#da77f2",cyan:"#63e6be",white:"#d6d6d6",brightBlack:"#4a4a4a",brightRed:"#ff8787",brightGreen:"#8ce99a",brightYellow:"#ffe066",brightBlue:"#74c0fc",brightMagenta:"#e599f7",brightCyan:"#7ae5d5",brightWhite:"#f8f9fa"}}),C=new be.FitAddon;u.loadAddon(C),u.open(f),C.fit(),T.current=u;let R=we.getState().token;if(!R)try{R=JSON.parse(localStorage.getItem("user-storage")||"")?.state?.token||""}catch{}const $=`${window.location.protocol==="https:"?"wss:":"ws:"}//${window.location.host}/ws/audit/${d}?token=${encodeURIComponent(R||"")}`;console.debug("[LiveMonitor] Connecting to:",$.replace(/token=[^&]+/,"token=***"));const N=new WebSocket($);h.current=N;let z=!1;N.onopen=()=>{console.debug("[LiveMonitor] WS connected for session:",d),z=!0,b(!0),j(""),u.writeln("\x1B[36m[审计监控] 已连接到会话...\x1B[0m")},N.onmessage=x=>{try{const g=JSON.parse(x.data);switch(g.type){case"session_info":r(g.payload),u.writeln(`\x1B[36m用户: ${g.payload.username} 服务器: ${g.payload.server_name}\x1B[0m`);break;case"history":g.data&&u.write(g.data);break;case"output":g.data&&u.write(g.data);break}}catch{}},N.onerror=x=>{console.debug("[LiveMonitor] WS error:",x),j("WebSocket 连接失败")},N.onclose=x=>{console.debug("[LiveMonitor] WS closed code:",x.code,"reason:",x.reason),z||j(`连接失败 (code=${x.code}): ${x.reason||"认证失败或网络问题"}`),b(!1),u.writeln(`\r
\x1B[33m[审计监控] 连接已断开\x1B[0m`)};const _=setTimeout(()=>{z||(j("连接超时 — 请确认已登录且有活跃会话"),N.close())},5e3);return()=>{clearTimeout(_),N.close(),u.dispose(),T.current=null,h.current=null,b(!1)}},[s,d]);const m=f=>f?new Date(f).toLocaleString():"--";return e.jsx(ie,{"code-path":"src/components/system/LiveMonitorDialog.tsx:169:5",open:s,onOpenChange:o,children:e.jsxs(le,{"code-path":"src/components/system/LiveMonitorDialog.tsx:170:7",showOverlay:!1,className:"bg-[var(--dg-bg-secondary)] border-[var(--dg-border)] max-w-[95vw] max-h-[90vh] w-[1200px]",children:[e.jsxs(oe,{"code-path":"src/components/system/LiveMonitorDialog.tsx:171:9",children:[e.jsxs(ne,{"code-path":"src/components/system/LiveMonitorDialog.tsx:172:11",className:"text-[var(--dg-text-primary)] flex items-center gap-2",children:[e.jsx(Q,{"code-path":"src/components/system/LiveMonitorDialog.tsx:173:13",className:"h-5 w-5 text-emerald-400"}),"终端实时监控",L&&e.jsx(w,{"code-path":"src/components/system/LiveMonitorDialog.tsx:176:15",variant:"outline",className:"border-emerald-500/30 text-emerald-400 bg-emerald-500/10 ml-2 text-xs",children:"● 在线"}),!L&&!O&&e.jsxs(w,{"code-path":"src/components/system/LiveMonitorDialog.tsx:181:15",variant:"outline",className:"border-yellow-500/30 text-yellow-400 bg-yellow-500/10 ml-2 text-xs",children:[e.jsx(pe,{"code-path":"src/components/system/LiveMonitorDialog.tsx:182:17",className:"h-3 w-3 animate-spin mr-1"}),"连接中"]}),O&&e.jsx(w,{"code-path":"src/components/system/LiveMonitorDialog.tsx:187:15",variant:"outline",className:"border-red-500/30 text-red-400 bg-red-500/10 ml-2 text-xs",children:"连接失败"})]}),e.jsx(de,{"code-path":"src/components/system/LiveMonitorDialog.tsx:192:11",className:"text-[var(--dg-text-secondary)]",children:v&&e.jsxs("div",{"code-path":"src/components/system/LiveMonitorDialog.tsx:194:15",className:"flex items-center gap-4 text-xs mt-1 flex-wrap",children:[e.jsxs("span",{"code-path":"src/components/system/LiveMonitorDialog.tsx:195:17",className:"flex items-center gap-1",children:[e.jsx(Oe,{"code-path":"src/components/system/LiveMonitorDialog.tsx:196:19",className:"h-3 w-3"}),v.username]}),e.jsxs("span",{"code-path":"src/components/system/LiveMonitorDialog.tsx:199:17",className:"flex items-center gap-1",children:[e.jsx(Se,{"code-path":"src/components/system/LiveMonitorDialog.tsx:200:19",className:"h-3 w-3"}),v.server_name," (",v.server_ip,")"]}),e.jsxs("span",{"code-path":"src/components/system/LiveMonitorDialog.tsx:203:17",className:"flex items-center gap-1",children:[e.jsx(Ce,{"code-path":"src/components/system/LiveMonitorDialog.tsx:204:19",className:"h-3 w-3"}),m(v.start_time)]})]})})]}),e.jsx("div",{"code-path":"src/components/system/LiveMonitorDialog.tsx:212:9",ref:p,className:"w-full rounded-md overflow-hidden border border-[var(--dg-border)]",style:{height:"55vh"}})]})})}function je(s){if(!s||s<0)return"--";if(s<60)return`${s}秒`;const o=Math.floor(s/60),d=s%60;if(o<60)return`${o}分${d>0?`${d}秒`:""}`;const n=Math.floor(o/60),p=o%60;return`${n}时${p>0?`${p}分`:""}`}function Pe(s){return s?s<1024?`${s} B`:s<1024*1024?`${(s/1024).toFixed(1)} KB`:`${(s/1024/1024).toFixed(2)} MB`:"0 B"}function fe(s){return s==="active"?e.jsxs(w,{"code-path":"src/pages/TerminalAuditTab.tsx:84:7",variant:"outline",className:"border-emerald-500/30 text-emerald-400 bg-emerald-500/10",children:[e.jsx("span",{"code-path":"src/pages/TerminalAuditTab.tsx:85:9",className:"w-1.5 h-1.5 rounded-full bg-emerald-400 mr-1.5 inline-block animate-pulse"}),"活动中"]}):s==="failed"?e.jsx(w,{"code-path":"src/pages/TerminalAuditTab.tsx:92:7",variant:"outline",className:"border-red-500/30 text-red-400 bg-red-500/10",children:"失败"}):e.jsx(w,{"code-path":"src/pages/TerminalAuditTab.tsx:98:5",variant:"outline",className:"border-slate-500/30 text-slate-400 bg-slate-500/10",children:"已关闭"})}function Ke(s){const[o,d]=a.useState(0);return a.useEffect(()=>{if(!s)return;const n=new Date(s).getTime(),p=()=>d(Math.floor((Date.now()-n)/1e3));p();const T=setInterval(p,1e3);return()=>clearInterval(T)},[s]),o}const He=[{id:"active",name:"在线会话",icon:he},{id:"history",name:"历史会话",icon:Q},{id:"commands",name:"会话命令",icon:I}];function ce(){const s=xe(),[o,d]=a.useState("active"),[n,p]=a.useState([]),[T,h]=a.useState(!0),[L,b]=a.useState(""),[O,j]=a.useState([]),[v,r]=a.useState(!0),[m,f]=a.useState(""),[u,C]=a.useState(null),[R,S]=a.useState(!1),[$,N]=a.useState([]),[z,_]=a.useState(!0),[x,g]=a.useState(""),[B,V]=a.useState(""),[Y,c]=a.useState(!1),[k,Z]=a.useState(null),J=a.useCallback(async()=>{h(!0);try{const t=await F.list({page:1,page_size:100,status:"active"});p(t?.list??[])}catch{p([])}finally{h(!1)}},[]);a.useEffect(()=>{J()},[J]);const te=n.filter(t=>{const A=L.toLowerCase();return!A||(t.username||"").toLowerCase().includes(A)||(t.server_name||"").toLowerCase().includes(A)||(t.server_ip||"").toLowerCase().includes(A)}),Ae=async Line truncated