删除版本与更新功能: - routes/admin_api/update.py: 删除整个文件 - routes/admin_api/__init__.py: 移除 update 模块注册 - admin-frontend/src/pages/SystemPage.vue: 移除版本更新UI区块 - admin-frontend/src/api/update.js: 删除整个文件 - 删除 static/admin/assets/update-*.js 报表页自动刷新: - admin-frontend/src/pages/ReportPage.vue: 添加 setInterval 每1秒刷新 - 在 onMounted 启动定时器,onUnmounted 清除 - 覆盖统计数据、运行中任务、系统信息等所有动态数据 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
4 lines
18 KiB
JavaScript
4 lines
18 KiB
JavaScript
import{P as g,_ as He,r as d,c as de,o as Ke,e as p,H as Oe,f as w,g as u,h as r,j as a,w as l,p as s,F as pe,q as Ce,A as y,n as c,I as ae,m as Se,J as ce,K as k}from"./index-akVRSJTL.js";async function Je(){const{data:f}=await g.get("/admin/security/dashboard");return f}async function Ge(f){const{data:v}=await g.get("/admin/security/threats",{params:f});return v}async function Qe(){const{data:f}=await g.get("/admin/security/banned-ips");return f}async function We(){const{data:f}=await g.get("/admin/security/banned-users");return f}async function Xe(f){const{data:v}=await g.post("/admin/security/ban-ip",f);return v}async function Ye(f){const{data:v}=await g.post("/admin/security/unban-ip",{ip:f});return v}async function Ze(f){const{data:v}=await g.post("/admin/security/ban-user",f);return v}async function et(f){const{data:v}=await g.post("/admin/security/unban-user",{user_id:f});return v}async function tt(f){const v=encodeURIComponent(String(f||"").trim()),{data:I}=await g.get(`/admin/security/ip-risk/${v}`);return I}async function at(f){const v=encodeURIComponent(String(f||"").trim()),{data:I}=await g.get(`/admin/security/user-risk/${v}`);return I}async function lt(){const{data:f}=await g.post("/admin/security/cleanup",{});return f}const nt={class:"page-stack"},st={class:"app-page-title"},it={class:"toolbar"},ot={class:"stat-value"},ut={class:"stat-label"},rt={class:"filters"},dt={class:"table-wrap"},pt={key:1},ct={key:1},ft={class:"mono ellipsis"},mt={class:"ellipsis"},vt={class:"pagination"},_t={class:"page-hint app-muted"},yt={class:"toolbar"},bt={class:"table-wrap"},gt={class:"table-wrap"},kt={class:"filters"},ht={class:"filters"},wt={class:"risk-head"},It={class:"risk-title"},Vt={key:0},xt={key:1},Ct={class:"toolbar"},St={class:"table-wrap"},Ut={class:"mono ellipsis"},Pt={class:"ellipsis"},Tt={class:"dialog-actions"},fe=20,Bt={__name:"SecurityPage",setup(f){const v=d("threats"),I=d(!1),z=d(null),le=d(!1),E=d([]),A=d(0),S=d(1),$=d(""),R=d(""),D=d(!1),me=d([]),ve=d([]),_e=d("ips"),U=d(!1),N=d(!1),o=d({kind:"ip",ip:"",user_id:"",reason:"",duration_hours:24,permanent:!1}),H=d("ip"),V=d(!1),K=d(""),O=d(""),_=d(null),h=d(""),Ue=["sql_injection","xss","path_traversal","command_injection","ssrf","scanner","bruteforce","csrf","xxe","file_upload"];function ne(n){const e=Number(n);return Number.isFinite(e)?e:0}function F(n){const e=Number(n||0);return e>=80?{label:"高",type:"danger"}:e>=50?{label:"中",type:"warning"}:{label:"低",type:"success"}}function ye(n){const e=String(n||"").trim();return e||"永久"}function be(n){const e=[];return n?.field_name&&e.push(`字段: ${n.field_name}`),n?.rule&&e.push(`规则: ${n.rule}`),n?.matched&&e.push(`匹配: ${n.matched}`),n?.value_preview&&e.push(`值: ${n.value_preview}`),e.length?e.join(" · "):"-"}function J(n){const e=String(n?.request_method||"").trim(),i=String(n?.request_path||"").trim();return`${e} ${i}`.trim()||"-"}const Pe=de(()=>{const n=new Set(Ue),e=z.value?.recent_threat_events||[];for(const i of e){const b=String(i?.threat_type||"").trim();b&&n.add(b)}for(const i of E.value||[]){const b=String(i?.threat_type||"").trim();b&&n.add(b)}return Array.from(n).sort((i,b)=>i.localeCompare(b)).map(i=>({label:i,value:i}))}),Te=de(()=>{const n=z.value||{};return[{key:"threat_events_24h",label:"最近24小时威胁事件",value:ne(n.threat_events_24h)},{key:"banned_ip_count",label:"当前封禁IP数",value:ne(n.banned_ip_count)},{key:"banned_user_count",label:"当前封禁用户数",value:ne(n.banned_user_count)}]}),Be=de(()=>Math.max(1,Math.ceil((A.value||0)/fe)));async function j(){I.value=!0;try{z.value=await Je()}catch{z.value=null}finally{I.value=!1}}async function G(){le.value=!0;try{const n={page:S.value,per_page:fe};$.value&&(n.event_type=$.value),R.value&&(n.severity=R.value);const e=await Ge(n);E.value=e?.items||[],A.value=e?.total||0}catch{E.value=[],A.value=0}finally{le.value=!1}}async function P(){if(!D.value){D.value=!0;try{const[n,e]=await Promise.allSettled([Qe(),We()]);me.value=n.status==="fulfilled"?n.value?.items||[]:[],ve.value=e.status==="fulfilled"?e.value?.items||[]:[]}finally{D.value=!1}}}async function se(){await Promise.allSettled([j(),G(),P()])}function $e(){S.value=1,G()}function Re(){$.value="",R.value="",S.value=1,G()}function ge(){o.value={kind:"ip",ip:"",user_id:"",reason:"",duration_hours:24,permanent:!1}}function Q(n="ip",e={}){ge(),o.value.kind=n==="user"?"user":"ip",o.value.kind==="ip"?o.value.ip=String(e.ip||"").trim():o.value.user_id=String(e.user_id||"").trim(),e.reason&&(o.value.reason=String(e.reason||"").trim()),U.value=!0}async function De(){const n=o.value.kind,e=String(o.value.reason||"").trim(),i=!!o.value.permanent,b=Number(o.value.duration_hours||24);if(!e){k.error("原因不能为空");return}if(n==="ip"){const X=String(o.value.ip||"").trim();if(!X){k.error("IP不能为空");return}N.value=!0;try{await Xe({ip:X,reason:e,duration_hours:b,permanent:i}),k.success("IP已封禁"),U.value=!1,await Promise.allSettled([j(),P()])}catch{}finally{N.value=!1}return}const q=String(o.value.user_id||"").trim(),W=Number.parseInt(q,10);if(!Number.isFinite(W)){k.error("用户ID无效");return}N.value=!0;try{await Ze({user_id:W,reason:e,duration_hours:b,permanent:i}),k.success("用户已封禁"),U.value=!1,await Promise.allSettled([j(),P()])}catch{}finally{N.value=!1}}async function ke(n){const e=String(n||"").trim();if(e){try{await ce.confirm(`确定解除对 IP ${e} 的封禁吗?`,"解除封禁",{confirmButtonText:"解除",cancelButtonText:"取消",type:"warning"})}catch{return}try{await Ye(e),k.success("已解除IP封禁"),await Promise.allSettled([j(),P()])}catch{}}}async function he(n){const e=Number.parseInt(String(n||"").trim(),10);if(Number.isFinite(e)){try{await ce.confirm(`确定解除对 用户ID ${e} 的封禁吗?`,"解除封禁",{confirmButtonText:"解除",cancelButtonText:"取消",type:"warning"})}catch{return}try{await et(e),k.success("已解除用户封禁"),await Promise.allSettled([j(),P()])}catch{}}}function we(n){const e=String(n||"").trim();e&&(v.value="risk",H.value="ip",K.value=e,ie())}function Ie(n){const e=String(n||"").trim();e&&(v.value="risk",H.value="user",O.value=e,oe())}async function ie(){const n=String(K.value||"").trim();if(!n){k.error("请输入IP");return}V.value=!0;try{_.value=await tt(n),h.value="ip"}catch{_.value=null,h.value=""}finally{V.value=!1}}async function oe(){const n=String(O.value||"").trim(),e=Number.parseInt(n,10);if(!Number.isFinite(e)){k.error("请输入有效的用户ID");return}V.value=!0;try{_.value=await at(e),h.value="user"}catch{_.value=null,h.value=""}finally{V.value=!1}}function Ne(){!_.value||!h.value||(h.value==="ip"?Q("ip",{ip:_.value?.ip,reason:"风险查询手动封禁"}):Q("user",{user_id:_.value?.user_id,reason:"风险查询手动封禁"}))}async function Fe(){!_.value||!h.value||(h.value==="ip"?(await ke(_.value?.ip),await ie()):(await he(_.value?.user_id),await oe()))}const ue=d(!1);async function je(){try{await ce.confirm(`确定清理过期封禁记录,并衰减风险分吗?
|
||
|
||
该操作不会影响仍在有效期内的封禁。`,"清理过期记录",{confirmButtonText:"清理",cancelButtonText:"取消",type:"warning"})}catch{return}ue.value=!0;try{await lt(),k.success("清理完成"),await se()}catch{}finally{ue.value=!1}}return Ke(async()=>{await se()}),(n,e)=>{const i=p("el-button"),b=p("el-skeleton"),q=p("el-card"),W=p("el-col"),X=p("el-row"),T=p("el-option"),Ve=p("el-select"),m=p("el-table-column"),x=p("el-tag"),Y=p("el-link"),Z=p("el-tooltip"),ee=p("el-table"),qe=p("el-pagination"),C=p("el-tab-pane"),re=p("el-tabs"),L=p("el-input"),xe=p("el-radio-button"),Le=p("el-radio-group"),B=p("el-form-item"),Me=p("el-switch"),ze=p("el-input-number"),Ee=p("el-form"),Ae=p("el-dialog"),te=Oe("loading");return u(),w("div",nt,[r("div",st,[e[21]||(e[21]=r("h2",null,"安全防护",-1)),r("div",it,[a(i,{onClick:se},{default:l(()=>[...e[18]||(e[18]=[s("刷新",-1)])]),_:1}),a(i,{type:"warning",plain:"",loading:ue.value,onClick:je},{default:l(()=>[...e[19]||(e[19]=[s("清理过期记录",-1)])]),_:1},8,["loading"]),a(i,{type:"primary",onClick:e[0]||(e[0]=t=>Q())},{default:l(()=>[...e[20]||(e[20]=[s("手动封禁",-1)])]),_:1})])]),a(X,{gutter:12,class:"stats-row"},{default:l(()=>[(u(!0),w(pe,null,Ce(Te.value,t=>(u(),y(W,{key:t.key,xs:24,sm:8,md:8,lg:8,xl:8},{default:l(()=>[a(q,{shadow:"never",class:"stat-card","body-style":{padding:"14px"}},{default:l(()=>[r("div",ot,[I.value?(u(),y(b,{key:0,rows:1,animated:""})):(u(),w(pe,{key:1},[s(c(t.value),1)],64))]),r("div",ut,c(t.label),1)]),_:2},1024)]),_:2},1024))),128))]),_:1}),a(q,{shadow:"never","body-style":{padding:"16px"},class:"card"},{default:l(()=>[a(re,{modelValue:v.value,"onUpdate:modelValue":e[9]||(e[9]=t=>v.value=t)},{default:l(()=>[a(C,{label:"威胁事件",name:"threats"},{default:l(()=>[r("div",rt,[a(Ve,{modelValue:$.value,"onUpdate:modelValue":e[1]||(e[1]=t=>$.value=t),placeholder:"类型",style:{width:"220px"},filterable:"",clearable:"","allow-create":"","default-first-option":""},{default:l(()=>[a(T,{label:"全部",value:""}),(u(!0),w(pe,null,Ce(Pe.value,t=>(u(),y(T,{key:t.value,label:t.label,value:t.value},null,8,["label","value"]))),128))]),_:1},8,["modelValue"]),a(Ve,{modelValue:R.value,"onUpdate:modelValue":e[2]||(e[2]=t=>R.value=t),placeholder:"严重程度",style:{width:"200px"},clearable:""},{default:l(()=>[a(T,{label:"全部",value:""}),a(T,{label:"高风险(>=80)",value:"high"}),a(T,{label:"中风险(50-79)",value:"medium"}),a(T,{label:"低风险(<50)",value:"low"})]),_:1},8,["modelValue"]),a(i,{type:"primary",onClick:$e},{default:l(()=>[...e[22]||(e[22]=[s("筛选",-1)])]),_:1}),a(i,{onClick:Re},{default:l(()=>[...e[23]||(e[23]=[s("重置",-1)])]),_:1})]),r("div",dt,[ae((u(),y(ee,{data:E.value,style:{width:"100%"}},{default:l(()=>[a(m,{prop:"created_at",label:"时间",width:"180"}),a(m,{label:"类型",width:"170"},{default:l(({row:t})=>[a(x,{effect:"light",type:"info"},{default:l(()=>[s(c(t.threat_type||"unknown"),1)]),_:2},1024)]),_:1}),a(m,{label:"严重程度",width:"120"},{default:l(({row:t})=>[a(x,{type:F(t.score).type,effect:"light"},{default:l(()=>[s(c(F(t.score).label)+" ("+c(t.score??0)+") ",1)]),_:2},1032,["type"])]),_:1}),a(m,{label:"IP",width:"150"},{default:l(({row:t})=>[t.ip?(u(),y(Y,{key:0,type:"primary",underline:!1,onClick:M=>we(t.ip)},{default:l(()=>[s(c(t.ip),1)]),_:2},1032,["onClick"])):(u(),w("span",pt,"-"))]),_:1}),a(m,{label:"用户",width:"120"},{default:l(({row:t})=>[t.user_id!==null&&t.user_id!==void 0?(u(),y(Y,{key:0,type:"primary",underline:!1,onClick:M=>Ie(t.user_id)},{default:l(()=>[s(c(t.user_id),1)]),_:2},1032,["onClick"])):(u(),w("span",ct,"-"))]),_:1}),a(m,{label:"操作路径","min-width":"220"},{default:l(({row:t})=>[a(Z,{content:J(t),placement:"top","show-after":300},{default:l(()=>[r("span",ft,c(J(t)),1)]),_:2},1032,["content"])]),_:1}),a(m,{label:"Payload预览","min-width":"240"},{default:l(({row:t})=>[a(Z,{content:be(t),placement:"top","show-after":300},{default:l(()=>[r("span",mt,c(t.value_preview||"-"),1)]),_:2},1032,["content"])]),_:1})]),_:1},8,["data"])),[[te,le.value]])]),r("div",vt,[a(qe,{"current-page":S.value,"onUpdate:currentPage":e[3]||(e[3]=t=>S.value=t),"page-size":fe,total:A.value,layout:"prev, pager, next, jumper, ->, total",onCurrentChange:G},null,8,["current-page","total"]),r("div",_t,"第 "+c(S.value)+" / "+c(Be.value)+" 页",1)])]),_:1}),a(C,{label:"封禁管理",name:"bans"},{default:l(()=>[r("div",yt,[a(i,{onClick:P},{default:l(()=>[...e[24]||(e[24]=[s("刷新封禁列表",-1)])]),_:1}),a(i,{type:"primary",onClick:e[4]||(e[4]=t=>Q())},{default:l(()=>[...e[25]||(e[25]=[s("手动封禁",-1)])]),_:1})]),a(re,{modelValue:_e.value,"onUpdate:modelValue":e[5]||(e[5]=t=>_e.value=t),class:"inner-tabs"},{default:l(()=>[a(C,{label:"IP黑名单",name:"ips"},{default:l(()=>[r("div",bt,[ae((u(),y(ee,{data:me.value,style:{width:"100%"}},{default:l(()=>[a(m,{label:"IP",width:"180"},{default:l(({row:t})=>[a(Y,{type:"primary",underline:!1,onClick:M=>we(t.ip)},{default:l(()=>[s(c(t.ip||"-"),1)]),_:2},1032,["onClick"])]),_:1}),a(m,{prop:"reason",label:"原因","min-width":"260"}),a(m,{label:"过期时间",width:"190"},{default:l(({row:t})=>[s(c(ye(t.expires_at)),1)]),_:1}),a(m,{label:"操作",width:"120",fixed:"right"},{default:l(({row:t})=>[a(i,{size:"small",type:"danger",plain:"",onClick:M=>ke(t.ip)},{default:l(()=>[...e[26]||(e[26]=[s("解除",-1)])]),_:1},8,["onClick"])]),_:1})]),_:1},8,["data"])),[[te,D.value]])])]),_:1}),a(C,{label:"用户黑名单",name:"users"},{default:l(()=>[r("div",gt,[ae((u(),y(ee,{data:ve.value,style:{width:"100%"}},{default:l(()=>[a(m,{label:"用户ID",width:"180"},{default:l(({row:t})=>[a(Y,{type:"primary",underline:!1,onClick:M=>Ie(t.user_id)},{default:l(()=>[s(c(t.user_id??"-"),1)]),_:2},1032,["onClick"])]),_:1}),a(m,{prop:"reason",label:"原因","min-width":"260"}),a(m,{label:"过期时间",width:"190"},{default:l(({row:t})=>[s(c(ye(t.expires_at)),1)]),_:1}),a(m,{label:"操作",width:"120",fixed:"right"},{default:l(({row:t})=>[a(i,{size:"small",type:"danger",plain:"",onClick:M=>he(t.user_id)},{default:l(()=>[...e[27]||(e[27]=[s("解除",-1)])]),_:1},8,["onClick"])]),_:1})]),_:1},8,["data"])),[[te,D.value]])])]),_:1})]),_:1},8,["modelValue"])]),_:1}),a(C,{label:"风险查询",name:"risk"},{default:l(()=>[a(re,{modelValue:H.value,"onUpdate:modelValue":e[8]||(e[8]=t=>H.value=t),class:"inner-tabs"},{default:l(()=>[a(C,{label:"IP查询",name:"ip"},{default:l(()=>[r("div",kt,[a(L,{modelValue:K.value,"onUpdate:modelValue":e[6]||(e[6]=t=>K.value=t),placeholder:"输入IP,如 1.2.3.4",style:{width:"260px"},clearable:""},null,8,["modelValue"]),a(i,{type:"primary",loading:V.value,onClick:ie},{default:l(()=>[...e[28]||(e[28]=[s("查询",-1)])]),_:1},8,["loading"])])]),_:1}),a(C,{label:"用户查询",name:"user"},{default:l(()=>[r("div",ht,[a(L,{modelValue:O.value,"onUpdate:modelValue":e[7]||(e[7]=t=>O.value=t),placeholder:"输入用户ID,如 123",style:{width:"260px"},clearable:""},null,8,["modelValue"]),a(i,{type:"primary",loading:V.value,onClick:oe},{default:l(()=>[...e[29]||(e[29]=[s("查询",-1)])]),_:1},8,["loading"])])]),_:1})]),_:1},8,["modelValue"]),_.value?(u(),y(q,{key:0,shadow:"never","body-style":{padding:"16px"},class:"sub-card"},{default:l(()=>[r("div",wt,[r("div",It,[h.value==="ip"?(u(),w("strong",Vt,"IP: "+c(_.value.ip),1)):(u(),w("strong",xt,"用户ID: "+c(_.value.user_id),1)),e[32]||(e[32]=r("span",{class:"app-muted"},"风险分",-1)),a(x,{type:F(_.value.risk_score).type,effect:"light"},{default:l(()=>[s(c(_.value.risk_score??0),1)]),_:1},8,["type"]),_.value.is_banned?(u(),y(x,{key:2,type:"danger",effect:"light"},{default:l(()=>[...e[30]||(e[30]=[s("已封禁",-1)])]),_:1})):(u(),y(x,{key:3,type:"success",effect:"light"},{default:l(()=>[...e[31]||(e[31]=[s("未封禁",-1)])]),_:1}))]),r("div",Ct,[_.value.is_banned?(u(),y(i,{key:1,type:"danger",plain:"",onClick:Fe},{default:l(()=>[...e[34]||(e[34]=[s("解除封禁",-1)])]),_:1})):(u(),y(i,{key:0,type:"primary",plain:"",onClick:Ne},{default:l(()=>[...e[33]||(e[33]=[s("封禁",-1)])]),_:1}))])]),r("div",St,[ae((u(),y(ee,{data:_.value.threat_history||[],style:{width:"100%"}},{default:l(()=>[a(m,{prop:"created_at",label:"时间",width:"180"}),a(m,{label:"类型",width:"170"},{default:l(({row:t})=>[a(x,{effect:"light",type:"info"},{default:l(()=>[s(c(t.threat_type||"unknown"),1)]),_:2},1024)]),_:1}),a(m,{label:"严重程度",width:"120"},{default:l(({row:t})=>[a(x,{type:F(t.score).type,effect:"light"},{default:l(()=>[s(c(F(t.score).label)+" ("+c(t.score??0)+") ",1)]),_:2},1032,["type"])]),_:1}),a(m,{label:"操作路径","min-width":"220"},{default:l(({row:t})=>[a(Z,{content:J(t),placement:"top","show-after":300},{default:l(()=>[r("span",Ut,c(J(t)),1)]),_:2},1032,["content"])]),_:1}),a(m,{label:"Payload预览","min-width":"240"},{default:l(({row:t})=>[a(Z,{content:be(t),placement:"top","show-after":300},{default:l(()=>[r("span",Pt,c(t.value_preview||"-"),1)]),_:2},1032,["content"])]),_:1})]),_:1},8,["data"])),[[te,V.value]])])]),_:1})):Se("",!0)]),_:1})]),_:1},8,["modelValue"])]),_:1}),a(Ae,{modelValue:U.value,"onUpdate:modelValue":e[17]||(e[17]=t=>U.value=t),title:"手动封禁",width:"min(520px, 92vw)",onClosed:ge},{footer:l(()=>[r("div",Tt,[e[39]||(e[39]=r("div",{class:"spacer"},null,-1)),a(i,{onClick:e[16]||(e[16]=t=>U.value=!1)},{default:l(()=>[...e[37]||(e[37]=[s("取消",-1)])]),_:1}),a(i,{type:"primary",loading:N.value,onClick:De},{default:l(()=>[...e[38]||(e[38]=[s("确认封禁",-1)])]),_:1},8,["loading"])])]),default:l(()=>[a(Ee,{"label-width":"120px"},{default:l(()=>[a(B,{label:"类型"},{default:l(()=>[a(Le,{modelValue:o.value.kind,"onUpdate:modelValue":e[10]||(e[10]=t=>o.value.kind=t)},{default:l(()=>[a(xe,{label:"ip"},{default:l(()=>[...e[35]||(e[35]=[s("IP",-1)])]),_:1}),a(xe,{label:"user"},{default:l(()=>[...e[36]||(e[36]=[s("用户",-1)])]),_:1})]),_:1},8,["modelValue"])]),_:1}),o.value.kind==="ip"?(u(),y(B,{key:0,label:"IP"},{default:l(()=>[a(L,{modelValue:o.value.ip,"onUpdate:modelValue":e[11]||(e[11]=t=>o.value.ip=t),placeholder:"例如 1.2.3.4"},null,8,["modelValue"])]),_:1})):(u(),y(B,{key:1,label:"用户ID"},{default:l(()=>[a(L,{modelValue:o.value.user_id,"onUpdate:modelValue":e[12]||(e[12]=t=>o.value.user_id=t),placeholder:"例如 123"},null,8,["modelValue"])]),_:1})),a(B,{label:"原因"},{default:l(()=>[a(L,{modelValue:o.value.reason,"onUpdate:modelValue":e[13]||(e[13]=t=>o.value.reason=t),type:"textarea",rows:3,placeholder:"请输入封禁原因"},null,8,["modelValue"])]),_:1}),a(B,{label:"永久封禁"},{default:l(()=>[a(Me,{modelValue:o.value.permanent,"onUpdate:modelValue":e[14]||(e[14]=t=>o.value.permanent=t)},null,8,["modelValue"])]),_:1}),o.value.permanent?Se("",!0):(u(),y(B,{key:2,label:"持续(小时)"},{default:l(()=>[a(ze,{modelValue:o.value.duration_hours,"onUpdate:modelValue":e[15]||(e[15]=t=>o.value.duration_hours=t),min:1,max:8760},null,8,["modelValue"])]),_:1}))]),_:1})]),_:1},8,["modelValue"])])}}},Rt=He(Bt,[["__scopeId","data-v-ea9240bd"]]);export{Rt as default};
|