import{a as g,_ as qe}from"./index-UGawl5hI.js";import{M as ze}from"./MetricGrid-Db4tVG3V.js";import{e as d,f as oe,g as Ee,r as v,W as Ae,l as P,o as c,k as p,j as a,w as l,C as i,F as Oe,m as Ge,c as b,X as ee,B as f,i as ue,E as te,b as w}from"./vendor-BczUEOE_.js";async function He(){const{data:u}=await g.get("/admin/security/dashboard");return u}async function Ke(u){const{data:m}=await g.get("/admin/security/threats",{params:u});return m}async function We(){const{data:u}=await g.get("/admin/security/banned-ips");return u}async function Xe(){const{data:u}=await g.get("/admin/security/banned-users");return u}async function Je(u){const{data:m}=await g.post("/admin/security/ban-ip",u);return m}async function Qe(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"},ht={class:"risk-title"},wt={key:0},It={key:1},Vt={class:"toolbar"},xt={class:"table-wrap"},Ct={class:"mono ellipsis"},St={class:"ellipsis"},Tt={class:"dialog-actions"},re=20,Pt={__name:"SecurityPage",setup(u){const m=d("threats"),V=d(!1),L=d(null),ae=d(!1),q=d([]),z=d(0),C=d(1),U=d(""),B=d(""),$=d(!1),de=d([]),pe=d([]),ce=d("ips"),S=d(!1),R=d(!1),o=d({kind:"ip",ip:"",user_id:"",reason:"",duration_hours:24,permanent:!1}),E=d("ip"),k=d(!1),A=d(""),O=d(""),_=d(null),h=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 D(n){const e=Number(n||0);return e>=80?{label:"高",type:"danger"}:e>=50?{label:"中",type:"warning"}:{label:"低",type:"success"}}function fe(n){const e=String(n||"").trim();return e||"永久"}function me(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 G(n){const e=String(n?.request_method||"").trim(),s=String(n?.request_path||"").trim();return`${e} ${s}`.trim()||"-"}const Ve=oe(()=>{const n=new Set(Ie),e=L.value?.recent_threat_events||[];for(const s of e){const y=String(s?.threat_type||"").trim();y&&n.add(y)}for(const s of q.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=oe(()=>{const n=L.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=oe(()=>Math.max(1,Math.ceil((z.value||0)/re)));async function N(){V.value=!0;try{L.value=await He()}catch{L.value=null}finally{V.value=!1}}async function H(){ae.value=!0;try{const n={page:C.value,per_page:re};U.value&&(n.event_type=U.value),B.value&&(n.severity=B.value);const e=await Ke(n);q.value=e?.items||[],z.value=e?.total||0}catch{q.value=[],z.value=0}finally{ae.value=!1}}async function F(){if(!$.value){$.value=!0;try{const[n,e]=await Promise.allSettled([We(),Xe()]);de.value=n.status==="fulfilled"?n.value?.items||[]:[],pe.value=e.status==="fulfilled"?e.value?.items||[]:[]}finally{$.value=!1}}}async function ve(){await Promise.allSettled([N(),H(),F()])}function Se(){C.value=1,H()}function Te(){U.value="",B.value="",C.value=1,H()}function _e(){o.value={kind:"ip",ip:"",user_id:"",reason:"",duration_hours:24,permanent:!1}}function K(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){w.error("原因不能为空");return}if(n==="ip"){const I=String(o.value.ip||"").trim();if(!I){w.error("IP不能为空");return}R.value=!0;try{await Je({ip:I,reason:e,duration_hours:y,permanent:s}),w.success("IP已封禁"),S.value=!1,await Promise.allSettled([N(),F()])}catch{}finally{R.value=!1}return}const X=String(o.value.user_id||"").trim(),r=Number.parseInt(X,10);if(!Number.isFinite(r)){w.error("用户ID无效");return}R.value=!0;try{await Ye({user_id:r,reason:e,duration_hours:y,permanent:s}),w.success("用户已封禁"),S.value=!1,await Promise.allSettled([N(),F()])}catch{}finally{R.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 Qe(e),w.success("已解除IP封禁"),await Promise.allSettled([N(),F()])}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),w.success("已解除用户封禁"),await Promise.allSettled([N(),F()])}catch{}}}function ge(n){const e=String(n||"").trim();e&&(m.value="risk",E.value="ip",A.value=e,W())}function ke(n){const e=String(n||"").trim();e&&(m.value="risk",E.value="user",O.value=e,ne())}async function W(){const n=String(A.value||"").trim();if(!n){w.error("请输入IP");return}k.value=!0;try{_.value=await et(n),h.value="ip"}catch{_.value=null,h.value=""}finally{k.value=!1}}async function ne(){const n=String(O.value||"").trim(),e=Number.parseInt(n,10);if(!Number.isFinite(e)){w.error("请输入有效的用户ID");return}k.value=!0;try{_.value=await at(e),h.value="user"}catch{_.value=null,h.value=""}finally{k.value=!1}}function Ue(){!_.value||!h.value||(h.value==="ip"?K("ip",{ip:_.value?.ip,reason:"风险查询手动封禁"}):K("user",{user_id:_.value?.user_id,reason:"风险查询手动封禁"}))}async function Be(){!_.value||!h.value||(h.value==="ip"?(await ye(_.value?.ip),await W()):(await be(_.value?.user_id),await ne()))}async function $e(){if(h.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),w.success("IP风险分已清零")}catch{}finally{k.value=!1}await W()}}}const ie=d(!1);async function Re(){try{await te.confirm(`确定清理过期封禁记录,并衰减风险分吗? 该操作不会影响仍在有效期内的封禁。`,"清理过期记录",{confirmButtonText:"清理",cancelButtonText:"取消",type:"warning"})}catch{return}ie.value=!0;try{await lt(),w.success("清理完成"),await ve()}catch{}finally{ie.value=!1}}return Ee(async()=>{await ve()}),(n,e)=>{const s=v("el-button"),y=v("el-option"),X=v("el-select"),r=v("el-table-column"),I=v("el-tag"),J=v("el-link"),Q=v("el-tooltip"),Y=v("el-table"),De=v("el-pagination"),x=v("el-tab-pane"),se=v("el-tabs"),M=v("el-input"),he=v("el-card"),we=v("el-radio-button"),Ne=v("el-radio-group"),T=v("el-form-item"),Fe=v("el-switch"),Me=v("el-input-number"),je=v("el-form"),Le=v("el-dialog"),Z=Ae("loading");return c(),P("div",nt,[p("div",it,[e[20]||(e[20]=p("h2",null,"安全防护",-1)),p("div",st,[a(s,{type:"warning",plain:"",loading:ie.value,onClick:Re},{default:l(()=>[...e[18]||(e[18]=[i("清理过期记录",-1)])]),_:1},8,["loading"]),a(s,{type:"primary",onClick:e[0]||(e[0]=t=>K())},{default:l(()=>[...e[19]||(e[19]=[i("手动封禁",-1)])]),_:1})])]),a(ze,{items:xe.value,loading:V.value,"min-width":220},null,8,["items","loading"]),a(he,{shadow:"never","body-style":{padding:"16px"},class:"card"},{default:l(()=>[a(se,{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(X,{modelValue:U.value,"onUpdate:modelValue":e[1]||(e[1]=t=>U.value=t),placeholder:"类型",style:{width:"220px"},filterable:"",clearable:"","allow-create":"","default-first-option":""},{default:l(()=>[a(y,{label:"全部",value:""}),(c(!0),P(Oe,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(X,{modelValue:B.value,"onUpdate:modelValue":e[2]||(e[2]=t=>B.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[21]||(e[21]=[i("筛选",-1)])]),_:1}),a(s,{onClick:Te},{default:l(()=>[...e[22]||(e[22]=[i("重置",-1)])]),_:1})]),p("div",ut,[ee((c(),b(Y,{data:q.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:D(t.score).type,effect:"light"},{default:l(()=>[i(f(D(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(J,{key:0,type:"primary",underline:!1,onClick:j=>ge(t.ip)},{default:l(()=>[i(f(t.ip),1)]),_:2},1032,["onClick"])):(c(),P("span",rt,"-"))]),_:1}),a(r,{label:"用户",width:"120"},{default:l(({row:t})=>[t.user_id!==null&&t.user_id!==void 0?(c(),b(J,{key:0,type:"primary",underline:!1,onClick:j=>ke(t.user_id)},{default:l(()=>[i(f(t.user_id),1)]),_:2},1032,["onClick"])):(c(),P("span",dt,"-"))]),_:1}),a(r,{label:"操作路径","min-width":"220"},{default:l(({row:t})=>[a(Q,{content:G(t),placement:"top","show-after":300},{default:l(()=>[p("span",pt,f(G(t)),1)]),_:2},1032,["content"])]),_:1}),a(r,{label:"Payload预览","min-width":"240"},{default:l(({row:t})=>[a(Q,{content:me(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":re,total:z.value,layout:"prev, pager, next, jumper, ->, total",onCurrentChange:H},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,{type:"primary",onClick:e[4]||(e[4]=t=>K())},{default:l(()=>[...e[23]||(e[23]=[i("手动封禁",-1)])]),_:1})]),a(se,{modelValue:ce.value,"onUpdate:modelValue":e[5]||(e[5]=t=>ce.value=t),class:"inner-tabs"},{default:l(()=>[a(x,{label:"IP黑名单",name:"ips"},{default:l(()=>[p("div",_t,[ee((c(),b(Y,{data:de.value,style:{width:"100%"}},{default:l(()=>[a(r,{label:"IP",width:"180"},{default:l(({row:t})=>[a(J,{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(fe(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[24]||(e[24]=[i("解除",-1)])]),_:1},8,["onClick"])]),_:1})]),_:1},8,["data"])),[[Z,$.value]])])]),_:1}),a(x,{label:"用户黑名单",name:"users"},{default:l(()=>[p("div",yt,[ee((c(),b(Y,{data:pe.value,style:{width:"100%"}},{default:l(()=>[a(r,{label:"用户ID",width:"180"},{default:l(({row:t})=>[a(J,{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(fe(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[25]||(e[25]=[i("解除",-1)])]),_:1},8,["onClick"])]),_:1})]),_:1},8,["data"])),[[Z,$.value]])])]),_:1})]),_:1},8,["modelValue"])]),_:1}),a(x,{label:"风险查询",name:"risk"},{default:l(()=>[a(se,{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:W},{default:l(()=>[...e[26]||(e[26]=[i("查询",-1)])]),_:1},8,["loading"])])]),_:1}),a(x,{label:"用户查询",name:"user"},{default:l(()=>[p("div",gt,[a(M,{modelValue:O.value,"onUpdate:modelValue":e[7]||(e[7]=t=>O.value=t),placeholder:"输入用户ID,如 123",style:{width:"260px"},clearable:""},null,8,["modelValue"]),a(s,{type:"primary",loading:k.value,onClick:ne},{default:l(()=>[...e[27]||(e[27]=[i("查询",-1)])]),_:1},8,["loading"])])]),_:1})]),_:1},8,["modelValue"]),_.value?(c(),b(he,{key:0,shadow:"never","body-style":{padding:"16px"},class:"sub-card"},{default:l(()=>[p("div",kt,[p("div",ht,[h.value==="ip"?(c(),P("strong",wt,"IP: "+f(_.value.ip),1)):(c(),P("strong",It,"用户ID: "+f(_.value.user_id),1)),e[30]||(e[30]=p("span",{class:"app-muted"},"风险分",-1)),a(I,{type:D(_.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[28]||(e[28]=[i("已封禁",-1)])]),_:1})):(c(),b(I,{key:3,type:"success",effect:"light"},{default:l(()=>[...e[29]||(e[29]=[i("未封禁",-1)])]),_:1}))]),p("div",Vt,[_.value.is_banned?(c(),b(s,{key:1,type:"danger",plain:"",onClick:Be},{default:l(()=>[...e[32]||(e[32]=[i("解除封禁",-1)])]),_:1})):(c(),b(s,{key:0,type:"primary",plain:"",onClick:Ue},{default:l(()=>[...e[31]||(e[31]=[i("封禁",-1)])]),_:1})),h.value==="ip"?(c(),b(s,{key:2,type:"warning",plain:"",loading:k.value,onClick:$e},{default:l(()=>[...e[33]||(e[33]=[i(" 清除风险分 ",-1)])]),_:1},8,["loading"])):ue("",!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:D(t.score).type,effect:"light"},{default:l(()=>[i(f(D(t.score).label)+" ("+f(t.score??0)+") ",1)]),_:2},1032,["type"])]),_:1}),a(r,{label:"操作路径","min-width":"220"},{default:l(({row:t})=>[a(Q,{content:G(t),placement:"top","show-after":300},{default:l(()=>[p("span",Ct,f(G(t)),1)]),_:2},1032,["content"])]),_:1}),a(r,{label:"Payload预览","min-width":"240"},{default:l(({row:t})=>[a(Q,{content:me(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})):ue("",!0)]),_:1})]),_:1},8,["modelValue"])]),_:1}),a(Le,{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[38]||(e[38]=p("div",{class:"spacer"},null,-1)),a(s,{onClick:e[16]||(e[16]=t=>S.value=!1)},{default:l(()=>[...e[36]||(e[36]=[i("取消",-1)])]),_:1}),a(s,{type:"primary",loading:R.value,onClick:Pe},{default:l(()=>[...e[37]||(e[37]=[i("确认封禁",-1)])]),_:1},8,["loading"])])]),default:l(()=>[a(je,{"label-width":"120px"},{default:l(()=>[a(T,{label:"类型"},{default:l(()=>[a(Ne,{modelValue:o.value.kind,"onUpdate:modelValue":e[10]||(e[10]=t=>o.value.kind=t)},{default:l(()=>[a(we,{label:"ip"},{default:l(()=>[...e[34]||(e[34]=[i("IP",-1)])]),_:1}),a(we,{label:"user"},{default:l(()=>[...e[35]||(e[35]=[i("用户",-1)])]),_:1})]),_:1},8,["modelValue"])]),_:1}),o.value.kind==="ip"?(c(),b(T,{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(T,{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(T,{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(T,{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?ue("",!0):(c(),b(T,{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"])])}}},Rt=qe(Pt,[["__scopeId","data-v-94e0bde6"]]);export{Rt as default};