import{a as g,_ as Le,r as d,c as ue,o as ze,f as v,G as Ee,g as U,h as c,j as p,m as a,w as l,q as i,F as Ae,K as Ge,A as b,H as ee,p as f,n as re,I as te,J as h}from"./index-Bd3qzysl.js";import{M as He}from"./MetricGrid-W4bphIvI.js";async function Ke(){const{data:u}=await g.get("/admin/security/dashboard");return u}async function Oe(u){const{data:m}=await g.get("/admin/security/threats",{params:u});return m}async function Je(){const{data:u}=await g.get("/admin/security/banned-ips");return u}async function Qe(){const{data:u}=await g.get("/admin/security/banned-users");return u}async function We(u){const{data:m}=await g.post("/admin/security/ban-ip",u);return m}async function Xe(u){const{data:m}=await g.post("/admin/security/unban-ip",{ip:u});return m}async function Ye(u){const{data:m}=await g.post("/admin/security/ban-user",u);return m}async function Ze(u){const{data:m}=await g.post("/admin/security/unban-user",{user_id:u});return m}async function et(u){const m=encodeURIComponent(String(u||"").trim()),{data:V}=await g.get(`/admin/security/ip-risk/${m}`);return V}async function tt(u){const{data:m}=await g.post("/admin/security/ip-risk/clear",{ip:u});return m}async function at(u){const m=encodeURIComponent(String(u||"").trim()),{data:V}=await g.get(`/admin/security/user-risk/${m}`);return V}async function lt(){const{data:u}=await g.post("/admin/security/cleanup",{});return u}const nt={class:"page-stack"},it={class:"app-page-title"},st={class:"toolbar"},ot={class:"filters"},ut={class:"table-wrap"},rt={key:1},dt={key:1},pt={class:"mono ellipsis"},ct={class:"ellipsis"},ft={class:"pagination"},mt={class:"page-hint app-muted"},vt={class:"toolbar"},_t={class:"table-wrap"},yt={class:"table-wrap"},bt={class:"filters"},gt={class:"filters"},kt={class:"risk-head"},wt={class:"risk-title"},ht={key:0},It={key:1},Vt={class:"toolbar"},xt={class:"table-wrap"},Ct={class:"mono ellipsis"},St={class:"ellipsis"},Tt={class:"dialog-actions"},de=20,Pt={__name:"SecurityPage",setup(u){const m=d("threats"),V=d(!1),q=d(null),ae=d(!1),L=d([]),z=d(0),C=d(1),B=d(""),$=d(""),R=d(!1),pe=d([]),ce=d([]),fe=d("ips"),S=d(!1),D=d(!1),o=d({kind:"ip",ip:"",user_id:"",reason:"",duration_hours:24,permanent:!1}),E=d("ip"),k=d(!1),A=d(""),G=d(""),_=d(null),w=d(""),Ie=["sql_injection","xss","path_traversal","command_injection","ssrf","scanner","bruteforce","csrf","xxe","file_upload"];function le(n){const e=Number(n);return Number.isFinite(e)?e:0}function N(n){const e=Number(n||0);return e>=80?{label:"高",type:"danger"}:e>=50?{label:"中",type:"warning"}:{label:"低",type:"success"}}function me(n){const e=String(n||"").trim();return e||"永久"}function ve(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 H(n){const e=String(n?.request_method||"").trim(),s=String(n?.request_path||"").trim();return`${e} ${s}`.trim()||"-"}const Ve=ue(()=>{const n=new Set(Ie),e=q.value?.recent_threat_events||[];for(const s of e){const y=String(s?.threat_type||"").trim();y&&n.add(y)}for(const s of L.value||[]){const y=String(s?.threat_type||"").trim();y&&n.add(y)}return Array.from(n).sort((s,y)=>s.localeCompare(y)).map(s=>({label:s,value:s}))}),xe=ue(()=>{const n=q.value||{};return[{key:"threat_events_24h",label:"最近24小时威胁事件",value:le(n.threat_events_24h),tone:"red",hint:"用于衡量当前攻击面活跃度"},{key:"banned_ip_count",label:"当前封禁 IP 数",value:le(n.banned_ip_count),tone:"orange",hint:"自动与人工封禁总量"},{key:"banned_user_count",label:"当前封禁用户数",value:le(n.banned_user_count),tone:"purple",hint:"高风险账户拦截情况"}]}),Ce=ue(()=>Math.max(1,Math.ceil((z.value||0)/de)));async function F(){V.value=!0;try{q.value=await Ke()}catch{q.value=null}finally{V.value=!1}}async function K(){ae.value=!0;try{const n={page:C.value,per_page:de};B.value&&(n.event_type=B.value),$.value&&(n.severity=$.value);const e=await Oe(n);L.value=e?.items||[],z.value=e?.total||0}catch{L.value=[],z.value=0}finally{ae.value=!1}}async function T(){if(!R.value){R.value=!0;try{const[n,e]=await Promise.allSettled([Je(),Qe()]);pe.value=n.status==="fulfilled"?n.value?.items||[]:[],ce.value=e.status==="fulfilled"?e.value?.items||[]:[]}finally{R.value=!1}}}async function ne(){await Promise.allSettled([F(),K(),T()])}function Se(){C.value=1,K()}function Te(){B.value="",$.value="",C.value=1,K()}function _e(){o.value={kind:"ip",ip:"",user_id:"",reason:"",duration_hours:24,permanent:!1}}function O(n="ip",e={}){_e(),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()),S.value=!0}async function Pe(){const n=o.value.kind,e=String(o.value.reason||"").trim(),s=!!o.value.permanent,y=Number(o.value.duration_hours||24);if(!e){h.error("原因不能为空");return}if(n==="ip"){const I=String(o.value.ip||"").trim();if(!I){h.error("IP不能为空");return}D.value=!0;try{await We({ip:I,reason:e,duration_hours:y,permanent:s}),h.success("IP已封禁"),S.value=!1,await Promise.allSettled([F(),T()])}catch{}finally{D.value=!1}return}const Q=String(o.value.user_id||"").trim(),r=Number.parseInt(Q,10);if(!Number.isFinite(r)){h.error("用户ID无效");return}D.value=!0;try{await Ye({user_id:r,reason:e,duration_hours:y,permanent:s}),h.success("用户已封禁"),S.value=!1,await Promise.allSettled([F(),T()])}catch{}finally{D.value=!1}}async function ye(n){const e=String(n||"").trim();if(e){try{await te.confirm(`确定解除对 IP ${e} 的封禁吗?`,"解除封禁",{confirmButtonText:"解除",cancelButtonText:"取消",type:"warning"})}catch{return}try{await Xe(e),h.success("已解除IP封禁"),await Promise.allSettled([F(),T()])}catch{}}}async function be(n){const e=Number.parseInt(String(n||"").trim(),10);if(Number.isFinite(e)){try{await te.confirm(`确定解除对 用户ID ${e} 的封禁吗?`,"解除封禁",{confirmButtonText:"解除",cancelButtonText:"取消",type:"warning"})}catch{return}try{await Ze(e),h.success("已解除用户封禁"),await Promise.allSettled([F(),T()])}catch{}}}function ge(n){const e=String(n||"").trim();e&&(m.value="risk",E.value="ip",A.value=e,J())}function ke(n){const e=String(n||"").trim();e&&(m.value="risk",E.value="user",G.value=e,ie())}async function J(){const n=String(A.value||"").trim();if(!n){h.error("请输入IP");return}k.value=!0;try{_.value=await et(n),w.value="ip"}catch{_.value=null,w.value=""}finally{k.value=!1}}async function ie(){const n=String(G.value||"").trim(),e=Number.parseInt(n,10);if(!Number.isFinite(e)){h.error("请输入有效的用户ID");return}k.value=!0;try{_.value=await at(e),w.value="user"}catch{_.value=null,w.value=""}finally{k.value=!1}}function Ue(){!_.value||!w.value||(w.value==="ip"?O("ip",{ip:_.value?.ip,reason:"风险查询手动封禁"}):O("user",{user_id:_.value?.user_id,reason:"风险查询手动封禁"}))}async function Be(){!_.value||!w.value||(w.value==="ip"?(await ye(_.value?.ip),await J()):(await be(_.value?.user_id),await ie()))}async function $e(){if(w.value!=="ip")return;const n=String(_.value?.ip||"").trim();if(n){try{await te.confirm(`确定清除 IP ${n} 的风险分吗? 清除风险分不会删除威胁历史,也不会解除封禁。`,"清除风险分",{confirmButtonText:"清除",cancelButtonText:"取消",type:"warning"})}catch{return}if(!k.value){k.value=!0;try{await tt(n),h.success("IP风险分已清零")}catch{}finally{k.value=!1}await J()}}}const se=d(!1);async function Re(){try{await te.confirm(`确定清理过期封禁记录,并衰减风险分吗? 该操作不会影响仍在有效期内的封禁。`,"清理过期记录",{confirmButtonText:"清理",cancelButtonText:"取消",type:"warning"})}catch{return}se.value=!0;try{await lt(),h.success("清理完成"),await ne()}catch{}finally{se.value=!1}}return ze(async()=>{await ne()}),(n,e)=>{const s=v("el-button"),y=v("el-option"),Q=v("el-select"),r=v("el-table-column"),I=v("el-tag"),W=v("el-link"),X=v("el-tooltip"),Y=v("el-table"),De=v("el-pagination"),x=v("el-tab-pane"),oe=v("el-tabs"),M=v("el-input"),we=v("el-card"),he=v("el-radio-button"),Ne=v("el-radio-group"),P=v("el-form-item"),Fe=v("el-switch"),Me=v("el-input-number"),je=v("el-form"),qe=v("el-dialog"),Z=Ee("loading");return c(),U("div",nt,[p("div",it,[e[21]||(e[21]=p("h2",null,"安全防护",-1)),p("div",st,[a(s,{onClick:ne},{default:l(()=>[...e[18]||(e[18]=[i("刷新",-1)])]),_:1}),a(s,{type:"warning",plain:"",loading:se.value,onClick:Re},{default:l(()=>[...e[19]||(e[19]=[i("清理过期记录",-1)])]),_:1},8,["loading"]),a(s,{type:"primary",onClick:e[0]||(e[0]=t=>O())},{default:l(()=>[...e[20]||(e[20]=[i("手动封禁",-1)])]),_:1})])]),a(He,{items:xe.value,loading:V.value,"min-width":220},null,8,["items","loading"]),a(we,{shadow:"never","body-style":{padding:"16px"},class:"card"},{default:l(()=>[a(oe,{modelValue:m.value,"onUpdate:modelValue":e[9]||(e[9]=t=>m.value=t)},{default:l(()=>[a(x,{label:"威胁事件",name:"threats"},{default:l(()=>[p("div",ot,[a(Q,{modelValue:B.value,"onUpdate:modelValue":e[1]||(e[1]=t=>B.value=t),placeholder:"类型",style:{width:"220px"},filterable:"",clearable:"","allow-create":"","default-first-option":""},{default:l(()=>[a(y,{label:"全部",value:""}),(c(!0),U(Ae,null,Ge(Ve.value,t=>(c(),b(y,{key:t.value,label:t.label,value:t.value},null,8,["label","value"]))),128))]),_:1},8,["modelValue"]),a(Q,{modelValue:$.value,"onUpdate:modelValue":e[2]||(e[2]=t=>$.value=t),placeholder:"严重程度",style:{width:"200px"},clearable:""},{default:l(()=>[a(y,{label:"全部",value:""}),a(y,{label:"高风险(>=80)",value:"high"}),a(y,{label:"中风险(50-79)",value:"medium"}),a(y,{label:"低风险(<50)",value:"low"})]),_:1},8,["modelValue"]),a(s,{type:"primary",onClick:Se},{default:l(()=>[...e[22]||(e[22]=[i("筛选",-1)])]),_:1}),a(s,{onClick:Te},{default:l(()=>[...e[23]||(e[23]=[i("重置",-1)])]),_:1})]),p("div",ut,[ee((c(),b(Y,{data:L.value,style:{width:"100%"}},{default:l(()=>[a(r,{prop:"created_at",label:"时间",width:"180"}),a(r,{label:"类型",width:"170"},{default:l(({row:t})=>[a(I,{effect:"light",type:"info"},{default:l(()=>[i(f(t.threat_type||"unknown"),1)]),_:2},1024)]),_:1}),a(r,{label:"严重程度",width:"120"},{default:l(({row:t})=>[a(I,{type:N(t.score).type,effect:"light"},{default:l(()=>[i(f(N(t.score).label)+" ("+f(t.score??0)+") ",1)]),_:2},1032,["type"])]),_:1}),a(r,{label:"IP",width:"150"},{default:l(({row:t})=>[t.ip?(c(),b(W,{key:0,type:"primary",underline:!1,onClick:j=>ge(t.ip)},{default:l(()=>[i(f(t.ip),1)]),_:2},1032,["onClick"])):(c(),U("span",rt,"-"))]),_:1}),a(r,{label:"用户",width:"120"},{default:l(({row:t})=>[t.user_id!==null&&t.user_id!==void 0?(c(),b(W,{key:0,type:"primary",underline:!1,onClick:j=>ke(t.user_id)},{default:l(()=>[i(f(t.user_id),1)]),_:2},1032,["onClick"])):(c(),U("span",dt,"-"))]),_:1}),a(r,{label:"操作路径","min-width":"220"},{default:l(({row:t})=>[a(X,{content:H(t),placement:"top","show-after":300},{default:l(()=>[p("span",pt,f(H(t)),1)]),_:2},1032,["content"])]),_:1}),a(r,{label:"Payload预览","min-width":"240"},{default:l(({row:t})=>[a(X,{content:ve(t),placement:"top","show-after":300},{default:l(()=>[p("span",ct,f(t.value_preview||"-"),1)]),_:2},1032,["content"])]),_:1})]),_:1},8,["data"])),[[Z,ae.value]])]),p("div",ft,[a(De,{"current-page":C.value,"onUpdate:currentPage":e[3]||(e[3]=t=>C.value=t),"page-size":de,total:z.value,layout:"prev, pager, next, jumper, ->, total",onCurrentChange:K},null,8,["current-page","total"]),p("div",mt,"第 "+f(C.value)+" / "+f(Ce.value)+" 页",1)])]),_:1}),a(x,{label:"封禁管理",name:"bans"},{default:l(()=>[p("div",vt,[a(s,{onClick:T},{default:l(()=>[...e[24]||(e[24]=[i("刷新封禁列表",-1)])]),_:1}),a(s,{type:"primary",onClick:e[4]||(e[4]=t=>O())},{default:l(()=>[...e[25]||(e[25]=[i("手动封禁",-1)])]),_:1})]),a(oe,{modelValue:fe.value,"onUpdate:modelValue":e[5]||(e[5]=t=>fe.value=t),class:"inner-tabs"},{default:l(()=>[a(x,{label:"IP黑名单",name:"ips"},{default:l(()=>[p("div",_t,[ee((c(),b(Y,{data:pe.value,style:{width:"100%"}},{default:l(()=>[a(r,{label:"IP",width:"180"},{default:l(({row:t})=>[a(W,{type:"primary",underline:!1,onClick:j=>ge(t.ip)},{default:l(()=>[i(f(t.ip||"-"),1)]),_:2},1032,["onClick"])]),_:1}),a(r,{prop:"reason",label:"原因","min-width":"260"}),a(r,{label:"过期时间",width:"190"},{default:l(({row:t})=>[i(f(me(t.expires_at)),1)]),_:1}),a(r,{label:"操作",width:"120",fixed:"right"},{default:l(({row:t})=>[a(s,{size:"small",type:"danger",plain:"",onClick:j=>ye(t.ip)},{default:l(()=>[...e[26]||(e[26]=[i("解除",-1)])]),_:1},8,["onClick"])]),_:1})]),_:1},8,["data"])),[[Z,R.value]])])]),_:1}),a(x,{label:"用户黑名单",name:"users"},{default:l(()=>[p("div",yt,[ee((c(),b(Y,{data:ce.value,style:{width:"100%"}},{default:l(()=>[a(r,{label:"用户ID",width:"180"},{default:l(({row:t})=>[a(W,{type:"primary",underline:!1,onClick:j=>ke(t.user_id)},{default:l(()=>[i(f(t.user_id??"-"),1)]),_:2},1032,["onClick"])]),_:1}),a(r,{prop:"reason",label:"原因","min-width":"260"}),a(r,{label:"过期时间",width:"190"},{default:l(({row:t})=>[i(f(me(t.expires_at)),1)]),_:1}),a(r,{label:"操作",width:"120",fixed:"right"},{default:l(({row:t})=>[a(s,{size:"small",type:"danger",plain:"",onClick:j=>be(t.user_id)},{default:l(()=>[...e[27]||(e[27]=[i("解除",-1)])]),_:1},8,["onClick"])]),_:1})]),_:1},8,["data"])),[[Z,R.value]])])]),_:1})]),_:1},8,["modelValue"])]),_:1}),a(x,{label:"风险查询",name:"risk"},{default:l(()=>[a(oe,{modelValue:E.value,"onUpdate:modelValue":e[8]||(e[8]=t=>E.value=t),class:"inner-tabs"},{default:l(()=>[a(x,{label:"IP查询",name:"ip"},{default:l(()=>[p("div",bt,[a(M,{modelValue:A.value,"onUpdate:modelValue":e[6]||(e[6]=t=>A.value=t),placeholder:"输入IP,如 1.2.3.4",style:{width:"260px"},clearable:""},null,8,["modelValue"]),a(s,{type:"primary",loading:k.value,onClick:J},{default:l(()=>[...e[28]||(e[28]=[i("查询",-1)])]),_:1},8,["loading"])])]),_:1}),a(x,{label:"用户查询",name:"user"},{default:l(()=>[p("div",gt,[a(M,{modelValue:G.value,"onUpdate:modelValue":e[7]||(e[7]=t=>G.value=t),placeholder:"输入用户ID,如 123",style:{width:"260px"},clearable:""},null,8,["modelValue"]),a(s,{type:"primary",loading:k.value,onClick:ie},{default:l(()=>[...e[29]||(e[29]=[i("查询",-1)])]),_:1},8,["loading"])])]),_:1})]),_:1},8,["modelValue"]),_.value?(c(),b(we,{key:0,shadow:"never","body-style":{padding:"16px"},class:"sub-card"},{default:l(()=>[p("div",kt,[p("div",wt,[w.value==="ip"?(c(),U("strong",ht,"IP: "+f(_.value.ip),1)):(c(),U("strong",It,"用户ID: "+f(_.value.user_id),1)),e[32]||(e[32]=p("span",{class:"app-muted"},"风险分",-1)),a(I,{type:N(_.value.risk_score).type,effect:"light"},{default:l(()=>[i(f(_.value.risk_score??0),1)]),_:1},8,["type"]),_.value.is_banned?(c(),b(I,{key:2,type:"danger",effect:"light"},{default:l(()=>[...e[30]||(e[30]=[i("已封禁",-1)])]),_:1})):(c(),b(I,{key:3,type:"success",effect:"light"},{default:l(()=>[...e[31]||(e[31]=[i("未封禁",-1)])]),_:1}))]),p("div",Vt,[_.value.is_banned?(c(),b(s,{key:1,type:"danger",plain:"",onClick:Be},{default:l(()=>[...e[34]||(e[34]=[i("解除封禁",-1)])]),_:1})):(c(),b(s,{key:0,type:"primary",plain:"",onClick:Ue},{default:l(()=>[...e[33]||(e[33]=[i("封禁",-1)])]),_:1})),w.value==="ip"?(c(),b(s,{key:2,type:"warning",plain:"",loading:k.value,onClick:$e},{default:l(()=>[...e[35]||(e[35]=[i(" 清除风险分 ",-1)])]),_:1},8,["loading"])):re("",!0)])]),p("div",xt,[ee((c(),b(Y,{data:_.value.threat_history||[],style:{width:"100%"}},{default:l(()=>[a(r,{prop:"created_at",label:"时间",width:"180"}),a(r,{label:"类型",width:"170"},{default:l(({row:t})=>[a(I,{effect:"light",type:"info"},{default:l(()=>[i(f(t.threat_type||"unknown"),1)]),_:2},1024)]),_:1}),a(r,{label:"严重程度",width:"120"},{default:l(({row:t})=>[a(I,{type:N(t.score).type,effect:"light"},{default:l(()=>[i(f(N(t.score).label)+" ("+f(t.score??0)+") ",1)]),_:2},1032,["type"])]),_:1}),a(r,{label:"操作路径","min-width":"220"},{default:l(({row:t})=>[a(X,{content:H(t),placement:"top","show-after":300},{default:l(()=>[p("span",Ct,f(H(t)),1)]),_:2},1032,["content"])]),_:1}),a(r,{label:"Payload预览","min-width":"240"},{default:l(({row:t})=>[a(X,{content:ve(t),placement:"top","show-after":300},{default:l(()=>[p("span",St,f(t.value_preview||"-"),1)]),_:2},1032,["content"])]),_:1})]),_:1},8,["data"])),[[Z,k.value]])])]),_:1})):re("",!0)]),_:1})]),_:1},8,["modelValue"])]),_:1}),a(qe,{modelValue:S.value,"onUpdate:modelValue":e[17]||(e[17]=t=>S.value=t),title:"手动封禁",width:"min(520px, 92vw)",onClosed:_e},{footer:l(()=>[p("div",Tt,[e[40]||(e[40]=p("div",{class:"spacer"},null,-1)),a(s,{onClick:e[16]||(e[16]=t=>S.value=!1)},{default:l(()=>[...e[38]||(e[38]=[i("取消",-1)])]),_:1}),a(s,{type:"primary",loading:D.value,onClick:Pe},{default:l(()=>[...e[39]||(e[39]=[i("确认封禁",-1)])]),_:1},8,["loading"])])]),default:l(()=>[a(je,{"label-width":"120px"},{default:l(()=>[a(P,{label:"类型"},{default:l(()=>[a(Ne,{modelValue:o.value.kind,"onUpdate:modelValue":e[10]||(e[10]=t=>o.value.kind=t)},{default:l(()=>[a(he,{label:"ip"},{default:l(()=>[...e[36]||(e[36]=[i("IP",-1)])]),_:1}),a(he,{label:"user"},{default:l(()=>[...e[37]||(e[37]=[i("用户",-1)])]),_:1})]),_:1},8,["modelValue"])]),_:1}),o.value.kind==="ip"?(c(),b(P,{key:0,label:"IP"},{default:l(()=>[a(M,{modelValue:o.value.ip,"onUpdate:modelValue":e[11]||(e[11]=t=>o.value.ip=t),placeholder:"例如 1.2.3.4"},null,8,["modelValue"])]),_:1})):(c(),b(P,{key:1,label:"用户ID"},{default:l(()=>[a(M,{modelValue:o.value.user_id,"onUpdate:modelValue":e[12]||(e[12]=t=>o.value.user_id=t),placeholder:"例如 123"},null,8,["modelValue"])]),_:1})),a(P,{label:"原因"},{default:l(()=>[a(M,{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(P,{label:"永久封禁"},{default:l(()=>[a(Fe,{modelValue:o.value.permanent,"onUpdate:modelValue":e[14]||(e[14]=t=>o.value.permanent=t)},null,8,["modelValue"])]),_:1}),o.value.permanent?re("",!0):(c(),b(P,{key:2,label:"持续(小时)"},{default:l(()=>[a(Me,{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"])])}}},$t=Le(Pt,[["__scopeId","data-v-09b34cd6"]]);export{$t as default};