astrion-website/demo/static/dist/assets/adminApi.js

2 lines
11 KiB
JavaScript

import{d as he,o as _e,w as te,a as ke,b as r,c as i,u as $,e as ge,f as E,g as e,t as a,h as L,v as ye,i as se,j as ne,F as z,r as O,n as ae,k as c,l as j,m as be}from"./runtime-dom.esm-bundler.js";import{S as we,u as Ce}from"./SecondaryGate.js";import{_ as Ae}from"./_plugin-vue_export-helper.js";const xe={class:"api-page"},Ie={class:"api-header"},Te={class:"muted"},Be={class:"header-actions"},Ne=["disabled"],$e={class:"panel"},Ee={class:"metrics-grid"},Pe={class:"metric-card"},Se={class:"metric-card"},Ue={class:"metric-card"},Re={class:"muted"},Ve={class:"metric-card"},je={class:"muted"},Fe={class:"panel"},Me={class:"section-head"},qe={class:"create-form"},De=["disabled"],Le={class:"table-wrapper"},ze={key:0},Oe={key:0,class:"status-badge online"},Ge={key:1,class:"status-badge offline"},Ke={class:"token-cell"},Xe={class:"token-text"},Ze=["onClick","disabled"],Je=["onClick","disabled"],He=["onClick","disabled"],Qe={key:0,class:"error-text"},We={class:"panel grid"},Ye={class:"table-wrapper small scrollable"},et={key:0},tt={class:"upload-feed scrollable"},st={key:0,class:"upload-item"},nt={class:"muted small"},at={class:"muted small"},lt=he({__name:"ApiAdminApp",setup(rt){const{verified:v,configured:le,loading:oe,error:re,check:G,verify:ie}=Ce(),P=c(!1),S=c(!0);let p;const k=c(null),g=c([]),d=c({}),U=c(""),F=c(""),R=c(!1),m=c({username:"",note:""}),f=c(""),h=j(()=>{var s;return((s=k.value)==null?void 0:s.overview)||{}}),K=j(()=>{var s;return((s=k.value)==null?void 0:s.uploads)||{}}),X=j(()=>{var t;const s=((t=k.value)==null?void 0:t.containers)||{};return Object.keys(s).map(o=>({key:o,...s[o]||{}}))}),ue=j(()=>g.value.reduce((s,t)=>s+(t.usage&&t.usage.total_requests?t.usage.total_requests:0),0)),_=s=>{const t=Number(s||0);return Number.isFinite(t)?t.toLocaleString("zh-CN"):"0"},y=s=>{const t=Number(s||0);if(!t)return"0B";const o=["B","KB","MB","GB","TB"];let l=t,u=0;for(;l>=1024&&u<o.length-1;)l/=1024,u+=1;return`${l.toFixed(1)}${o[u]}`},de=s=>{if(!s)return"—";try{return new Date(s).toLocaleString()}catch{return s}},M=s=>{if(!s)return"—";const t=new Date(s).getTime();if(!Number.isFinite(t))return"—";const o=Date.now()-t,l=Math.floor(o/6e4);if(l<1)return"刚刚";if(l<60)return`${l} 分钟前`;const u=Math.floor(l/60);return u<24?`${u} 小时前`:`${Math.floor(u/24)} 天前`},Z=s=>{if(s==null)return"—";const t=Number(s);return Number.isFinite(t)?`${t.toFixed(1)}%`:"—"},ce=async()=>{if(v.value){P.value=!0;try{const s=await fetch("/api/admin/api-dashboard",{credentials:"same-origin"}),t=await s.json();if(!s.ok||!t.success)throw new Error(t.error||"加载失败");k.value=t.data}catch(s){console.error(s)}finally{P.value=!1}}},q=async()=>{if(v.value)try{const s=await fetch("/api/admin/api-users",{credentials:"same-origin"}),t=await s.json();if(!s.ok||!t.success)throw new Error(t.error||"加载失败");const o={};g.value=(t.data||[]).map(l=>({...l,usage:l.usage||{total_requests:0,endpoints:{},last_request_at:null},tokens:l.tokens||{total_tokens:0,input_tokens:0,output_tokens:0},storage:l.storage||{total_bytes:0}})),g.value.forEach(l=>{o[l.username]=d.value[l.username]||{token:"",visible:!1}}),d.value=o}catch(s){console.error(s)}},b=async()=>{await Promise.all([ce(),q()])},V=()=>{p&&(clearInterval(p),p=void 0),S.value&&v.value&&(p=window.setInterval(b,3e4))},ve=async()=>{if(!v.value)return;f.value="";const s=m.value.username.trim().toLowerCase();if(!s){f.value="请输入账号名";return}if(!/^[a-z0-9_-]{3,32}$/.test(s)){f.value="账号名需为 3-32 位小写字母/数字/_/-";return}R.value=!0;try{const o=await fetch("/api/admin/api-users",{method:"POST",credentials:"same-origin",headers:{"Content-Type":"application/json"},body:JSON.stringify({username:s,note:m.value.note})}),l=await o.json();if(!o.ok||!l.success)throw new Error(l.error||"创建失败");await q(),d.value[s]={token:l.data.token,visible:!0},m.value={username:"",note:""}}catch(o){f.value=o.message||"创建失败"}finally{R.value=!1}},pe=async s=>{if(v.value&&confirm(`确认删除 API 用户 ${s} 吗?`)){F.value=s;try{const t=await fetch(`/api/admin/api-users/${encodeURIComponent(s)}`,{method:"DELETE",credentials:"same-origin"}),o=await t.json();if(!t.ok||!o.success)throw new Error(o.error||"删除失败");await q()}catch(t){alert(t.message||"删除失败")}finally{F.value=""}}},J=async s=>{const t=d.value[s];if(t!=null&&t.visible){d.value[s]={...t||{},visible:!1};return}U.value=s;try{const o=await fetch(`/api/admin/api-users/${encodeURIComponent(s)}/token`,{credentials:"same-origin"}),l=await o.json();if(!o.ok||!l.success)throw new Error(l.error||"获取失败");d.value[s]={token:l.data.token,visible:!0}}catch(o){alert(o.message||"获取 token 失败")}finally{U.value=""}},me=async s=>{var l;const t=d.value[s];t!=null&&t.token||await J(s);const o=(l=d.value[s])==null?void 0:l.token;if(o)try{await navigator.clipboard.writeText(o)}catch(u){console.error(u)}},fe=async s=>{await ie(s),v.value&&(await b(),V())};return _e(async()=>{await G(),v.value&&(await b(),V())}),te(S,V),te(v,s=>{s?(b(),V()):p&&(clearInterval(p),p=void 0)}),ke(()=>{p&&clearInterval(p)}),(s,t)=>{var o,l,u,D,H,Q,W,Y;return r(),i("div",xe,[$(v)?E("",!0):(r(),ge(we,{key:0,configured:$(le),loading:$(oe),error:$(re),description:"请输入管理员二级密码后查看 API 调用与账户。",onVerify:fe,onRecheck:$(G)},null,8,["configured","loading","error","onRecheck"])),e("header",Ie,[e("div",null,[t[3]||(t[3]=e("h1",null,"API 管理面板",-1)),t[4]||(t[4]=e("p",null,"监控 Token / 容器 / 上传审计,并管理 API 账户。",-1)),e("small",Te,"数据时间:"+a(M((o=k.value)==null?void 0:o.generated_at)),1)]),e("div",Be,[e("label",null,[L(e("input",{type:"checkbox","onUpdate:modelValue":t[0]||(t[0]=n=>S.value=n)},null,512),[[ye,S.value]]),t[5]||(t[5]=se(" 自动刷新",-1))]),e("button",{type:"button",disabled:P.value,onClick:b},a(P.value?"刷新中...":"手动刷新"),9,Ne)])]),e("section",$e,[e("div",Ee,[e("div",Pe,[t[6]||(t[6]=e("p",null,"API 用户数",-1)),e("strong",null,a(((l=h.value.totals)==null?void 0:l.users)||0),1)]),e("div",Se,[t[7]||(t[7]=e("p",null,"总请求",-1)),e("strong",null,a(_(ue.value)),1)]),e("div",Ue,[t[8]||(t[8]=e("p",null,"累计 Token",-1)),e("strong",null,a(_(((u=h.value.token_totals)==null?void 0:u.total_tokens)||0)),1),e("span",Re,"输入 "+a(_(((D=h.value.token_totals)==null?void 0:D.input_tokens)||0))+" / 输出 "+a(_(((H=h.value.token_totals)==null?void 0:H.output_tokens)||0)),1)]),e("div",Ve,[t[9]||(t[9]=e("p",null,"活动容器",-1)),e("strong",null,a(((Q=h.value.totals)==null?void 0:Q.containers_active)||0),1),e("span",je,"可用槽位 "+a(((W=h.value.totals)==null?void 0:W.available_container_slots)??"—"),1)])])]),e("section",Fe,[e("div",Me,[t[10]||(t[10]=e("h2",null,"API 账户管理",-1)),e("div",qe,[L(e("input",{"onUpdate:modelValue":t[1]||(t[1]=n=>m.value.username=n),type:"text",placeholder:"新账号名 (小写英数/-/_)"},null,512),[[ne,m.value.username]]),L(e("input",{"onUpdate:modelValue":t[2]||(t[2]=n=>m.value.note=n),type:"text",placeholder:"备注(可选)"},null,512),[[ne,m.value.note]]),e("button",{type:"button",disabled:R.value,onClick:ve},a(R.value?"创建中...":"创建账号"),9,De)])]),e("div",Le,[e("table",null,[t[13]||(t[13]=e("thead",null,[e("tr",null,[e("th",null,"账号"),e("th",null,"备注"),e("th",null,"创建时间"),e("th",null,"请求"),e("th",null,"最近调用"),e("th",null,"Token 累计"),e("th",null,"存储"),e("th",null,"容器"),e("th",null,"密钥"),e("th",null,"操作")])],-1)),e("tbody",null,[g.value.length?E("",!0):(r(),i("tr",ze,[...t[11]||(t[11]=[e("td",{colspan:"10"},"暂无 API 账号",-1)])])),(r(!0),i(z,null,O(g.value,n=>{var w,C,A,x,I,T,B;return r(),i("tr",{key:n.username},[e("td",null,[e("strong",null,a(n.username),1)]),e("td",null,a(n.note||"—"),1),e("td",null,a(de(n.created_at)),1),e("td",null,a(_(((w=n.usage)==null?void 0:w.total_requests)||0)),1),e("td",null,a(M((C=n.usage)==null?void 0:C.last_request_at)),1),e("td",null,a(_(((A=n.tokens)==null?void 0:A.total_tokens)||0)),1),e("td",null,a(y((x=n.storage)==null?void 0:x.total_bytes)),1),e("td",null,[(I=n.container)!=null&&I.running?(r(),i("span",Oe,"运行中")):(r(),i("span",Ge,"未运行"))]),e("td",Ke,[e("span",Xe,a((T=d.value[n.username])!=null&&T.visible?((B=d.value[n.username])==null?void 0:B.token)||"—":"••••••••••"),1),e("button",{class:"icon-btn",onClick:N=>J(n.username),disabled:U.value===n.username},[...t[12]||(t[12]=[e("svg",{viewBox:"0 0 24 24",width:"18",height:"18","aria-hidden":"true"},[e("path",{d:"M1 12s4-7 11-7 11 7 11 7-4 7-11 7S1 12 1 12Zm11 3a3 3 0 1 0 0-6 3 3 0 0 0 0 6Z",fill:"none",stroke:"currentColor","stroke-width":"1.5"})],-1)])],8,Ze),e("button",{class:"icon-btn",onClick:N=>me(n.username),disabled:U.value===n.username}," 📋 ",8,Je)]),e("td",null,[e("button",{class:"link danger",onClick:N=>pe(n.username),disabled:F.value===n.username}," 删除 ",8,He)])])}),128))])])]),f.value?(r(),i("p",Qe,a(f.value),1)):E("",!0)]),e("section",We,[e("div",null,[t[16]||(t[16]=e("div",{class:"section-head"},[e("h3",null,"容器状态"),e("small",{class:"muted"},"实时抓取")],-1)),e("div",Ye,[e("table",null,[t[15]||(t[15]=e("thead",null,[e("tr",null,[e("th",null,"Key"),e("th",null,"CPU"),e("th",null,"内存%"),e("th",null,"内存"),e("th",null,"网络 RX/TX"),e("th",null,"状态")])],-1)),e("tbody",null,[X.value.length?E("",!0):(r(),i("tr",et,[...t[14]||(t[14]=[e("td",{colspan:"6"},"暂无容器",-1)])])),(r(!0),i(z,null,O(X.value,n=>{var w,C,A,x,I,T,B,N,ee;return r(),i("tr",{key:n.key},[e("td",null,a(n.key),1),e("td",null,a(Z((w=n.stats)==null?void 0:w.cpu_percent)),1),e("td",null,a(Z((A=(C=n.stats)==null?void 0:C.memory)==null?void 0:A.percent)),1),e("td",null,a(y((I=(x=n.stats)==null?void 0:x.memory)==null?void 0:I.used_bytes)),1),e("td",null,a(y((B=(T=n.stats)==null?void 0:T.net_io)==null?void 0:B.rx_bytes))+" / "+a(y((ee=(N=n.stats)==null?void 0:N.net_io)==null?void 0:ee.tx_bytes)),1),e("td",null,[e("span",{class:ae(["status-badge",n.running?"online":"offline"])},a(n.running?"运行中":"未运行"),3)])])}),128))])])])]),e("div",null,[t[17]||(t[17]=e("div",{class:"section-head"},[e("h3",null,"上传审计"),e("small",{class:"muted"},"来自隔离区日志")],-1)),e("ul",tt,[(Y=K.value.recent_events)!=null&&Y.length?E("",!0):(r(),i("li",st,"暂无记录")),(r(!0),i(z,null,O(K.value.recent_events||[],n=>(r(),i("li",{key:n.upload_id,class:"upload-item"},[e("div",null,[e("strong",null,a(n.original_name||"未命名文件"),1),e("div",nt," 用户 "+a(n.username)+" · "+a(y(n.size))+" · "+a(n.source||"unknown"),1)]),e("div",at,[se(a(M(n.timestamp))+" ",1),e("span",{class:ae(["status-badge",n.accepted?"online":"danger"])},a(n.accepted?"通过":"阻断"),3)])]))),128))])])])])}}}),ot=Ae(lt,[["__scopeId","data-v-e3a0943c"]]);be(ot).mount("#admin-api-app");