b220a5a762
Co-authored-by: Cursor <cursoragent@cursor.com>
4 lines
32 KiB
JavaScript
4 lines
32 KiB
JavaScript
import{_ as e,t,v as n}from"./VSelect-qv3fL2oy.js";import{n as r,t as i}from"./VRow-B_1khu0W.js";import{$r as a,A as ee,At as o,B as s,Dr as c,F as l,H as u,Ir as d,M as te,Mt as ne,N as f,Nt as re,P as p,Q as m,R as h,T as ie,Tr as ae,Ur as g,Yr as _,Zt as v,_r as y,dr as b,fr as x,gr as S,hr as C,j as oe,jt as w,kr as T,lr as se,ot as ce,pr as E,qt as D,sr as O,t as k,ur as A,ut as j,z as M}from"./index-DC1GUjcg.js";import{i as N,n as P,r as F,t as I}from"./validation-DMwOxx4a.js";import{t as L}from"./VDialog-2336zotW.js";import{t as le}from"./VTextarea-Dde5kxXN.js";import{t as R}from"./VSwitch-CIWbYcTz.js";import{n as ue,r as z}from"./useTerminalQuickCommands-BdKTtE45.js";var de={key:1,class:`text-medium-emphasis text-body-2 mb-4`},B={class:`text-caption text-medium-emphasis mr-2`,style:{"min-width":`1.5rem`}},V={class:`d-flex flex-wrap ga-2`},fe=y({__name:`TerminalQuickCommandsSettings`,setup(t){let n=e(),{commands:r,loading:i,load:o,create:ne,update:re,remove:v,reorder:y,customCommands:w,builtinCommands:se}=ue(),D=g(!1),N=g(null),P=g(``),F=g(``),I=g(!1),le=g(!1),R=g(!1),fe=g(null),pe=g(``);function me(){N.value=null,P.value=``,F.value=``,D.value=!0}function he(e){N.value=e.id,P.value=e.name,F.value=e.cmd.replace(/\r$/,``),D.value=!0}async function ge(){if(!P.value.trim()||!F.value.trim()){n(`名称和命令不能为空`,`warning`);return}let e=F.value.trim();e.endsWith(`\r`)||(e+=`\r`),I.value=!0;try{N.value?await re(N.value,P.value,e):await ne(P.value,e),D.value=!1,n(`已保存`)}catch(e){n(e instanceof Error?e.message:`保存失败`,`error`)}finally{I.value=!1}}function _e(e){fe.value=e.id,pe.value=e.name,R.value=!0}async function ve(){if(fe.value){I.value=!0;try{await v(fe.value),R.value=!1,n(`已删除`)}catch(e){n(e instanceof Error?e.message:`删除失败`,`error`)}finally{I.value=!1}}}async function ye(e){let t=w();if(e<=0)return;let n=t.map(e=>e.id),r=n[e-1];n[e-1]=n[e],n[e]=r,await xe(n)}async function be(e){let t=w();if(e>=t.length-1)return;let n=t.map(e=>e.id),r=n[e+1];n[e+1]=n[e],n[e]=r,await xe(n)}async function xe(e){le.value=!0;try{await y(e)}catch(e){n(e instanceof Error?e.message:`排序失败`,`error`)}finally{le.value=!1}}return ae(()=>{o().catch(e=>n(e.message,`error`))}),(e,t)=>(c(),b(h,{id:`terminal-quick-commands`,elevation:`0`,border:``,rounded:`lg`,class:`mb-4`},{default:d(()=>[S(s,{class:`d-flex align-center flex-wrap ga-2`},{default:d(()=>[S(j,{class:`mr-1`},{default:d(()=>[...t[6]||=[C(`mdi-flash`,-1)]]),_:1}),t[8]||=C(` 终端快捷命令 `,-1),S(f),S(m,{size:`small`,color:`primary`,variant:`flat`,"prepend-icon":`mdi-plus`,onClick:me},{default:d(()=>[...t[7]||=[C(` 添加命令 `,-1)]]),_:1})]),_:1}),S(p),S(M,null,{default:d(()=>[t[9]||=A(`div`,{class:`text-body-2 text-medium-emphasis mb-4`},` 自定义命令显示在终端栏最前(优先级最高)。终端页仅可点击执行;在此添加、编辑、删除与调整顺序。 `,-1),_(i)?(c(),b(ce,{key:0,indeterminate:``,color:`primary`,class:`mb-3`})):x(``,!0),_(w)().length===0&&!_(i)?(c(),E(`div`,de,` 暂无自定义命令,点击「添加命令」创建。 `)):x(``,!0),_(w)().length?(c(),b(ie,{key:2,density:`compact`,class:`mb-4 pa-0`},{default:d(()=>[(c(!0),E(O,null,T(_(w)(),(e,t)=>(c(),b(ee,{key:e.id,border:``,rounded:`lg`,class:`mb-2`},{prepend:d(()=>[A(`span`,B,a(t+1),1)]),append:d(()=>[S(m,{icon:`mdi-arrow-up`,size:`x-small`,variant:`text`,disabled:t===0||le.value,onClick:e=>ye(t)},null,8,[`disabled`,`onClick`]),S(m,{icon:`mdi-arrow-down`,size:`x-small`,variant:`text`,disabled:t===_(w)().length-1||le.value,onClick:e=>be(t)},null,8,[`disabled`,`onClick`]),S(m,{icon:`mdi-pencil`,size:`x-small`,variant:`text`,onClick:t=>he(e)},null,8,[`onClick`]),S(m,{icon:`mdi-delete`,size:`x-small`,variant:`text`,color:`error`,onClick:t=>_e(e)},null,8,[`onClick`])]),default:d(()=>[S(oe,null,{default:d(()=>[C(a(e.name),1)]),_:2},1024),S(te,{class:`font-mono text-truncate`},{default:d(()=>[C(a(e.cmd),1)]),_:2},1024)]),_:2},1024))),128))]),_:1})):x(``,!0),t[10]||=A(`div`,{class:`text-subtitle-2 mb-2`},`系统内置(不可编辑)`,-1),A(`div`,V,[(c(!0),E(O,null,T(_(se)(),e=>(c(),b(l,{key:e.id,size:`small`,variant:`outlined`,label:``},{default:d(()=>[C(a(e.name),1)]),_:2},1024))),128))])]),_:1}),S(L,{modelValue:D.value,"onUpdate:modelValue":t[3]||=e=>D.value=e,"max-width":`520`},{default:d(()=>[S(h,{border:``},{default:d(()=>[S(s,null,{default:d(()=>[C(a(N.value?`编辑命令`:`添加快捷命令`),1)]),_:1}),S(M,null,{default:d(()=>[S(k,{modelValue:P.value,"onUpdate:modelValue":t[0]||=e=>P.value=e,label:`显示名称`,variant:`outlined`,density:`compact`,class:`mb-3`},null,8,[`modelValue`]),t[11]||=A(`div`,{class:`text-body-2 text-medium-emphasis mb-2`},`命令内容(发送时自动追加回车)`,-1),S(z,{modelValue:F.value,"onUpdate:modelValue":t[1]||=e=>F.value=e,multiline:``,rows:4,placeholder:`systemctl status nginx`},null,8,[`modelValue`])]),_:1}),S(u,null,{default:d(()=>[S(f),S(m,{variant:`text`,onClick:t[2]||=e=>D.value=!1},{default:d(()=>[...t[12]||=[C(`取消`,-1)]]),_:1}),S(m,{color:`primary`,variant:`flat`,loading:I.value,onClick:ge},{default:d(()=>[...t[13]||=[C(`保存`,-1)]]),_:1},8,[`loading`])]),_:1})]),_:1})]),_:1},8,[`modelValue`]),S(L,{modelValue:R.value,"onUpdate:modelValue":t[5]||=e=>R.value=e,"max-width":`400`},{default:d(()=>[S(h,{border:``},{default:d(()=>[S(s,null,{default:d(()=>[...t[14]||=[C(`删除命令`,-1)]]),_:1}),S(M,null,{default:d(()=>[C(`确定删除「`+a(pe.value)+`」?`,1)]),_:1}),S(u,null,{default:d(()=>[S(f),S(m,{variant:`text`,onClick:t[4]||=e=>R.value=!1},{default:d(()=>[...t[15]||=[C(`取消`,-1)]]),_:1}),S(m,{color:`error`,variant:`flat`,loading:I.value,onClick:ve},{default:d(()=>[...t[16]||=[C(`删除`,-1)]]),_:1},8,[`loading`])]),_:1})]),_:1})]),_:1},8,[`modelValue`])]),_:1}))}}),pe=[{key:`notify_alert_cpu`,label:`CPU 超阈值告警`,desc:`CPU 使用率超过设定阈值时推送`,group:`服务器资源`},{key:`notify_alert_mem`,label:`内存超阈值告警`,desc:`内存使用率超过设定阈值时推送`,group:`服务器资源`},{key:`notify_alert_disk`,label:`磁盘超阈值告警`,desc:`磁盘使用率超过设定阈值时推送`,group:`服务器资源`},{key:`notify_recovery`,label:`资源恢复正常`,desc:`CPU/内存/磁盘告警解除时推送`,group:`服务器资源`},{key:`notify_time_drift`,label:`时钟偏差严重 (≥60s)`,desc:`子机与主机时间偏差过大影响 TOTP/cron`,group:`服务器`},{key:`notify_system_redis`,label:`Redis 连接异常 / 恢复`,desc:`Redis 不可达或恢复时推送`,group:`系统`},{key:`notify_system_mysql`,label:`MySQL 连接异常 / 恢复`,desc:`MySQL 不可达或恢复时推送`,group:`系统`},{key:`notify_restart`,label:`Nexus 后端重启`,desc:`Python 服务崩溃重启成功/失败时推送`,group:`系统`},{key:`notify_script_complete`,label:`脚本执行完成`,desc:`脚本批量执行结束时推送汇总与看板链接(不含失败明细)`,group:`运维`}],me={class:`d-flex flex-wrap ga-2 mb-4`},he={class:`text-caption text-medium-emphasis font-weight-medium mt-2 mb-1`},ge={class:`d-flex flex-wrap align-center ga-2 mb-3`},_e={class:`d-flex flex-wrap ga-2`},ve={key:1,class:`text-caption text-medium-emphasis mt-2 mb-0`},ye={class:`d-flex align-center mb-3`},be={key:0,class:`text-center mb-3`},xe=[`src`],Se={key:0,class:`d-flex align-center justify-center ga-2 mt-2`},Ce={class:`text-caption pa-1 bg-grey-lighten-4 rounded`,style:{"user-select":`all`}},we={key:1},Te={key:1,class:`d-flex ga-2 align-center`},Ee={class:`d-flex align-center ga-2`},De={class:`d-flex flex-wrap ga-2 mb-3`},Oe={key:0,class:`mb-4`},ke={class:`text-caption text-medium-emphasis mb-2`},Ae={key:0},je={class:`d-flex flex-wrap ga-1`},Me={key:1,class:`text-caption text-medium-emphasis mb-4`},Ne={class:`text-caption text-medium-emphasis`},Pe={class:`text-body-2 mb-2`},Fe={class:`d-flex flex-wrap ga-1 mb-2`,style:{"max-height":`240px`,"overflow-y":`auto`}},Ie={key:0,class:`text-body-2 text-medium-emphasis`},Le=y({__name:`SettingsPage`,setup(te){let y=e(),{saveToServer:oe,previewSound:ue}=o(),z=D(),de=ne,B=g(w),V=g(!1),Le=g(!1),H=g({system_name:``,system_title:``,cpu_alert_threshold:80,mem_alert_threshold:80,disk_alert_threshold:80,db_pool_size:10,db_max_overflow:20,telegram_bot_token:``,telegram_chat_id:``}),U=g(``),W=g(!1),Re=g(!1),ze=g(!1),Be=g(!1),G=g(!1),Ve=g([]),K=g(!1),He=g(!1),q=g({}),Ue=g(null),J=g(null),We=g(!1),Ge=g(!1),Ke=se(()=>{let e={};for(let t of pe)e[t.group]||(e[t.group]=[]),e[t.group].push(t);return e});function qe(e){let t=String(e??`true`).trim().toLowerCase();return![`false`,`0`,`no`,`off`].includes(t)}let Y=g({current:``,new_pw:``,confirm:``,totp_code:``}),Je=g(!1),Ye=g(``),Xe=g(``),Ze=g(``),Qe=g(!1),$e=g(!1),et=g(``),tt=g(``),nt=g(!1),X=g(!1),rt=g(``),it=g(!1),at=g(!1),ot=g(!1),Z=g(``),st=g(``),Q=g([]),ct=g(null),lt=g(0),ut=g(!1),dt=g(!1),$=g(!1),ft=g([]),pt=se(()=>Q.value.slice(0,24));async function mt(){Le.value=!0;try{let e=await v.get(`/settings/`),t=Array.isArray(e)?e:[],n=Object.keys(H.value);for(let e of t){let t=e.key;if(t===`telegram_bot_token`){W.value=!!e.value_set,U.value=``;continue}if(t===`script_exec_complete_sound`){B.value=re(e.value);continue}if(t&&pe.some(e=>e.key===t)){q.value[t]=qe(e.value);continue}if(t&&n.includes(t)){let n=e.value;typeof n==`string`&&/^\d+(\.\d+)?$/.test(n)?H.value[t]=Number(n):H.value[t]=n}}}catch{y(`加载设置失败`,`error`)}finally{Le.value=!1}}async function ht(){try{let e=await v.get(`/settings/ip-allowlist`);ot.value=e.enabled||!1,Z.value=e.subscription_url||``,st.value=(e.manual_ips||[]).join(`
|
||
`),Q.value=e.subscription_ips||[],ct.value=e.last_refresh||null,lt.value=e.total_count??new Set([...e.manual_ips||[],...e.subscription_ips||[]]).size}catch{y(`加载白名单失败`,`error`)}}async function gt(){Re.value=!0;try{let e=Object.entries(H.value).filter(([e])=>[`system_name`,`system_title`,`cpu_alert_threshold`,`mem_alert_threshold`,`disk_alert_threshold`,`db_pool_size`,`db_max_overflow`].includes(e));for(let[t,n]of e)await v.put(`/settings/${t}`,{value:n});y(`设置已保存`)}catch(e){y(e instanceof Error?e.message:`保存失败`,`error`)}finally{Re.value=!1}}async function _t(e){let t=U.value.trim(),n=String(H.value.telegram_chat_id||``).trim();return e?.requireToken&&!t&&!W.value?(y(`请先填写 Bot Token`,`error`),!1):e?.requireChatId&&!n?(y(`请先填写或检测 Chat ID`,`error`),!1):(t&&(await v.put(`/settings/telegram_bot_token`,{value:t}),W.value=!0,U.value=``,K.value=!1),n&&await v.put(`/settings/telegram_chat_id`,{value:n}),!0)}async function vt(){We.value=!0;try{J.value=await v.get(`/settings/ops-patrol/status`)}catch{J.value=null}finally{We.value=!1}}async function yt(){Ge.value=!0;try{if(!await _t({requireToken:!0,requireChatId:!0}))return;let e=await v.post(`/settings/ops-patrol/sync-telegram`,{});J.value={...J.value||{},telegram_layer3_ready:e.telegram_layer3_ready,telegram_env_synced:e.env_status.env_synced,env_status:e.env_status,telegram_app_configured:!0},y(`巡检 Telegram 已同步至 .env`),await vt()}catch(e){y(e instanceof Error?e.message:`同步失败`,`error`)}finally{Ge.value=!1}}async function bt(){He.value=!0;try{let e=U.value.trim(),t=String(H.value.telegram_chat_id||``).trim();if(!e&&!t&&!W.value){y(`请先填写 Bot Token 或 Chat ID`,`warning`);return}await _t(),await vt(),y(`Telegram 配置已保存`)}catch(e){y(e instanceof Error?e.message:`保存失败`,`error`)}finally{He.value=!1}}async function xt(e){if(!e||V.value)return;let t=B.value;B.value=e,V.value=!0;try{await oe(e),y(`提示音设置已保存`)}catch(e){B.value=t,y(e instanceof Error?e.message:`保存失败`,`error`)}finally{V.value=!1}}function St(){ue(B.value)}async function Ct(e,t){let n=q.value[e];q.value[e]=t,Ue.value=e;try{await v.put(`/settings/${e}`,{value:t?`true`:`false`})}catch(t){q.value[e]=n,y(t instanceof Error?t.message:`保存失败,已撤销`,`error`)}finally{Ue.value=null}}async function wt(){ze.value=!0;try{if(!await _t({requireToken:!0,requireChatId:!0}))return;await v.post(`/settings/telegram/test`),y(`测试消息已发送`)}catch(e){y(e instanceof Error?e.message:`发送失败`,`error`)}finally{ze.value=!1}}async function Tt(){Be.value=!0;try{if(!await _t({requireToken:!0}))return;if(Ve.value=(await v.get(`/settings/telegram/chats`)).chats||[],!Ve.value.length){y(`未检测到对话,请先向 Bot 发送一条消息后再试`,`warning`);return}G.value=!0}catch(e){y(e instanceof Error?e.message:`检测失败`,`error`)}finally{Be.value=!1}}async function Et(e){let t=String(e.id);H.value.telegram_chat_id=t,G.value=!1;try{await v.put(`/settings/telegram_chat_id`,{value:t}),y(`已选择 Chat ID: ${t}`)}catch(e){y(e instanceof Error?e.message:`保存 Chat ID 失败`,`error`)}}async function Dt(){if(Y.value.new_pw!==Y.value.confirm){y(`两次密码不一致`,`error`);return}Je.value=!0;try{let e={current_password:Y.value.current,new_password:Y.value.new_pw};z.admin?.totp_enabled&&Y.value.totp_code&&(e.totp_code=Y.value.totp_code),await v.put(`/auth/password`,e),Y.value={current:``,new_pw:``,confirm:``,totp_code:``},y(`密码已修改`)}catch(e){y(e instanceof Error?e.message:`修改失败`,`error`)}finally{Je.value=!1}}async function Ot(){try{let e=await v.post(`/auth/totp/setup`);Ye.value=e.qr_code,Xe.value=e.secret||``}catch(e){y(e instanceof Error?e.message:`生成失败`,`error`)}}async function kt(){Qe.value=!0;try{await v.post(`/auth/totp/enable`,{code:Ze.value}),await z.fetchProfile(),Ye.value=``,Ze.value=``,y(`TOTP 已启用`)}catch(e){y(e.message||`启用失败`,`error`)}finally{Qe.value=!1}}function At(){$e.value=!0}async function jt(){nt.value=!0;try{await v.post(`/auth/totp/disable`,{admin_id:z.admin?.id,current_password:et.value,totp_code:tt.value}),await z.fetchProfile(),$e.value=!1,et.value=``,tt.value=``,y(`TOTP 已禁用`)}catch(e){y(e.message||`禁用失败`,`error`)}finally{nt.value=!1}}function Mt(){Xe.value&&(navigator.clipboard.writeText(Xe.value),y(`密钥已复制`))}async function Nt(){if(X.value){X.value=!1;return}it.value=!0;try{rt.value=(await v.post(`/settings/api-key/reveal`,{})).value,X.value=!0}catch(e){y(e instanceof Error?e.message:`获取 API Key 失败`,`error`)}finally{it.value=!1}}async function Pt(){if(K.value){K.value=!1,W.value&&(U.value=``);return}if(U.value.trim()){K.value=!0;return}if(!W.value){K.value=!0;return}at.value=!0;try{U.value=(await v.post(`/settings/telegram/reveal-token`,{})).value,K.value=!0}catch(e){y(e instanceof Error?e.message:`获取 Bot Token 失败`,`error`)}finally{at.value=!1}}function Ft(){if(!X.value||!rt.value){y(`请先点击眼睛图标显示 API Key`,`warning`);return}navigator.clipboard.writeText(rt.value),y(`已复制到剪贴板`)}async function It(){try{await v.post(`/settings/ip-allowlist/toggle?enabled=${ot.value?`true`:`false`}`)}catch(e){y(e instanceof Error?e.message:`操作失败`,`error`)}}async function Lt(){let e=Z.value.trim();if(!e){y(`请先填写订阅 URL`,`error`);return}dt.value=!0;try{if(ft.value=(await v.post(`/settings/ip-allowlist/parse-subscription`,{url:e})).hosts||[],!ft.value.length){y(`未能解析出任何节点`,`warning`);return}$.value=!0}catch(e){y(e instanceof Error?e.message:`解析失败`,`error`)}finally{dt.value=!1}}async function Rt(){ut.value=!0;try{let e={manual_ips:st.value.split(`
|
||
`).map(e=>e.trim()).filter(Boolean),subscription_url:Z.value.trim(),enabled:ot.value};await v.post(`/settings/ip-allowlist`,e),y(`白名单已保存`),$.value=!1,await ht()}catch(e){y(e instanceof Error?e.message:`保存失败`,`error`)}finally{ut.value=!1}}return ae(()=>{mt(),ht(),vt(),z.fetchProfile()}),(e,o)=>(c(),b(n,{fluid:``,class:`pa-6`},{default:d(()=>[S(i,null,{default:d(()=>[S(r,{cols:`12`,lg:`6`},{default:d(()=>[S(h,{elevation:`0`,border:``,rounded:`lg`,class:`mb-4`},{default:d(()=>[S(s,{class:`d-flex align-center`},{default:d(()=>[S(j,{class:`mr-2`},{default:d(()=>[...o[27]||=[C(`mdi-cog`,-1)]]),_:1}),o[28]||=C(` 系统设置 `,-1)]),_:1}),Le.value?(c(),b(ce,{key:0,indeterminate:``,color:`primary`})):x(``,!0),S(p),S(M,null,{default:d(()=>[S(k,{modelValue:H.value.system_name,"onUpdate:modelValue":o[0]||=e=>H.value.system_name=e,label:`系统名称`,variant:`outlined`,density:`compact`,class:`mb-3`,rules:[_(N)(`系统名称`)]},null,8,[`modelValue`,`rules`]),S(k,{modelValue:H.value.system_title,"onUpdate:modelValue":o[1]||=e=>H.value.system_title=e,label:`系统标题`,variant:`outlined`,density:`compact`,class:`mb-3`,rules:[_(N)(`系统标题`)]},null,8,[`modelValue`,`rules`]),S(p,{class:`my-4`}),o[29]||=A(`div`,{class:`text-subtitle-2 mb-3`},`告警阈值`,-1),S(i,{dense:``},{default:d(()=>[S(r,{cols:`4`},{default:d(()=>[S(k,{modelValue:H.value.cpu_alert_threshold,"onUpdate:modelValue":o[2]||=e=>H.value.cpu_alert_threshold=e,label:`CPU %`,variant:`outlined`,density:`compact`,type:`number`,rules:[_(N)(`阈值`),_(F)(1,100,`阈值`)]},null,8,[`modelValue`,`rules`])]),_:1}),S(r,{cols:`4`},{default:d(()=>[S(k,{modelValue:H.value.mem_alert_threshold,"onUpdate:modelValue":o[3]||=e=>H.value.mem_alert_threshold=e,label:`内存 %`,variant:`outlined`,density:`compact`,type:`number`,rules:[_(N)(`阈值`),_(F)(1,100,`阈值`)]},null,8,[`modelValue`,`rules`])]),_:1}),S(r,{cols:`4`},{default:d(()=>[S(k,{modelValue:H.value.disk_alert_threshold,"onUpdate:modelValue":o[4]||=e=>H.value.disk_alert_threshold=e,label:`磁盘 %`,variant:`outlined`,density:`compact`,type:`number`,rules:[_(N)(`阈值`),_(F)(1,100,`阈值`)]},null,8,[`modelValue`,`rules`])]),_:1})]),_:1}),S(p,{class:`my-4`}),o[30]||=A(`div`,{class:`text-subtitle-2 mb-3`},`连接池`,-1),S(i,{dense:``},{default:d(()=>[S(r,{cols:`6`},{default:d(()=>[S(k,{modelValue:H.value.db_pool_size,"onUpdate:modelValue":o[5]||=e=>H.value.db_pool_size=e,label:`连接池大小`,variant:`outlined`,density:`compact`,type:`number`,rules:[_(N)(),_(I)()]},null,8,[`modelValue`,`rules`])]),_:1}),S(r,{cols:`6`},{default:d(()=>[S(k,{modelValue:H.value.db_max_overflow,"onUpdate:modelValue":o[6]||=e=>H.value.db_max_overflow=e,label:`溢出连接数`,variant:`outlined`,density:`compact`,type:`number`,rules:[_(N)(),_(I)()]},null,8,[`modelValue`,`rules`])]),_:1})]),_:1})]),_:1}),S(u,{class:`px-4 pb-4`},{default:d(()=>[S(f),S(m,{color:`primary`,variant:`flat`,onClick:gt,loading:Re.value},{default:d(()=>[...o[31]||=[C(`保存设置`,-1)]]),_:1},8,[`loading`])]),_:1})]),_:1}),S(fe),S(h,{elevation:`0`,border:``,rounded:`lg`,class:`mb-4`},{default:d(()=>[S(s,{class:`d-flex align-center`},{default:d(()=>[S(j,{class:`mr-2`},{default:d(()=>[...o[32]||=[C(`mdi-bell-ring-outline`,-1)]]),_:1}),o[33]||=C(` 界面提示 `,-1)]),_:1}),S(p),S(M,null,{default:d(()=>[o[35]||=A(`p`,{class:`text-caption text-medium-emphasis mb-3`},` 脚本批量执行全部结束时,在浏览器播放提示音(仅当前登录的管理员会话,与 Telegram 推送无关)。 `,-1),S(t,{modelValue:B.value,"onUpdate:modelValue":[o[7]||=e=>B.value=e,xt],items:_(de),"item-title":`title`,"item-value":`value`,label:`脚本执行完成提示音`,variant:`outlined`,density:`compact`,"hide-details":``,class:`mb-3`,loading:V.value,disabled:V.value},null,8,[`modelValue`,`items`,`loading`,`disabled`]),S(m,{size:`small`,variant:`tonal`,"prepend-icon":`mdi-volume-high`,disabled:B.value===`off`||V.value,onClick:St},{default:d(()=>[...o[34]||=[C(` 试听 `,-1)]]),_:1},8,[`disabled`])]),_:1})]),_:1}),S(h,{elevation:`0`,border:``,rounded:`lg`},{default:d(()=>[S(s,{class:`d-flex align-center`},{default:d(()=>[S(j,{class:`mr-2`},{default:d(()=>[...o[36]||=[C(`mdi-send`,-1)]]),_:1}),o[37]||=C(` Telegram 推送 `,-1)]),_:1}),S(p),S(M,null,{default:d(()=>[S(k,{modelValue:U.value,"onUpdate:modelValue":o[8]||=e=>U.value=e,label:`Bot Token`,variant:`outlined`,density:`compact`,class:`mb-3`,placeholder:W.value?`已配置(留空保持不变)`:`输入 Bot Token`,type:K.value?`text`:`password`,"append-inner-icon":K.value?`mdi-eye-off`:`mdi-eye`,"onClick:appendInner":Pt},null,8,[`modelValue`,`placeholder`,`type`,`append-inner-icon`]),W.value&&!U.value?(c(),b(l,{key:0,size:`small`,color:`success`,variant:`tonal`,class:`mb-3`},{default:d(()=>[...o[38]||=[C(`Token 已配置`,-1)]]),_:1})):x(``,!0),S(k,{modelValue:H.value.telegram_chat_id,"onUpdate:modelValue":o[9]||=e=>H.value.telegram_chat_id=e,label:`Chat ID`,variant:`outlined`,density:`compact`,class:`mb-3`},null,8,[`modelValue`]),o[46]||=A(`p`,{class:`text-caption text-medium-emphasis mb-3`},` 在 Telegram 向 Bot 发送任意消息后,可点击下方按钮自动检测 Chat ID。 `,-1),A(`div`,me,[S(m,{size:`small`,variant:`tonal`,"prepend-icon":`mdi-radar`,onClick:Tt,loading:Be.value},{default:d(()=>[...o[39]||=[C(`检测 Chat ID`,-1)]]),_:1},8,[`loading`]),S(m,{size:`small`,variant:`tonal`,"prepend-icon":`mdi-message-text`,onClick:wt,loading:ze.value},{default:d(()=>[...o[40]||=[C(`发送测试消息`,-1)]]),_:1},8,[`loading`]),S(m,{size:`small`,variant:`tonal`,color:`primary`,"prepend-icon":`mdi-content-save`,onClick:bt,loading:He.value},{default:d(()=>[...o[41]||=[C(`保存 Telegram 配置`,-1)]]),_:1},8,[`loading`])]),S(p,{class:`my-3`}),o[47]||=A(`div`,{class:`text-subtitle-2 mb-1`},`告警通知项目`,-1),o[48]||=A(`p`,{class:`text-caption text-medium-emphasis mb-3`},` 关闭后该类告警仍记录日志,但不发送 Telegram 消息。Bot Token 未配置时全部静默。 `,-1),(c(!0),E(O,null,T(Ke.value,(e,t)=>(c(),E(O,{key:t},[A(`div`,he,a(t),1),(c(!0),E(O,null,T(e,e=>(c(),b(R,{key:e.key,"model-value":q.value[e.key]??!0,label:e.label,hint:e.desc,"persistent-hint":``,color:`primary`,density:`compact`,"hide-details":`auto`,class:`mb-1`,disabled:Ue.value===e.key,"onUpdate:modelValue":t=>Ct(e.key,!!t)},null,8,[`model-value`,`label`,`hint`,`disabled`,`onUpdate:modelValue`]))),128))],64))),128)),S(p,{class:`my-4`}),o[49]||=A(`div`,{class:`text-subtitle-2 mb-1`},`Layer 3 宿主机巡检`,-1),o[50]||=A(`p`,{class:`text-caption text-medium-emphasis mb-3`},[C(` 每分钟由宿主机 cron 执行 `),A(`code`,null,`health_monitor.sh`),C(`:连续 3 次 /health 失败则自动重启并推送 Telegram。 配置保存后会自动同步至 .env;亦可手动同步。未同步时脚本会从数据库读取 Telegram 配置。 `)],-1),A(`div`,ge,[S(l,{size:`small`,color:J.value?.telegram_layer3_ready?`success`:`warning`,variant:`tonal`},{default:d(()=>[C(a(J.value?.telegram_layer3_ready?`巡检 Telegram 就绪`:`请先配置 Telegram`),1)]),_:1},8,[`color`]),J.value?(c(),b(l,{key:0,size:`small`,color:J.value.telegram_env_synced?`success`:`grey`,variant:`tonal`},{default:d(()=>[C(a(J.value.telegram_env_synced?`.env 已同步`:`.env 未同步(可用 DB 回退)`),1)]),_:1},8,[`color`])):x(``,!0)]),A(`div`,_e,[S(m,{size:`small`,variant:`tonal`,"prepend-icon":`mdi-sync`,onClick:yt,loading:Ge.value,disabled:!J.value?.telegram_app_configured},{default:d(()=>[...o[42]||=[C(` 同步巡检 Telegram `,-1)]]),_:1},8,[`loading`,`disabled`]),S(m,{size:`small`,variant:`text`,"prepend-icon":`mdi-refresh`,onClick:vt,loading:We.value},{default:d(()=>[...o[43]||=[C(` 刷新状态 `,-1)]]),_:1},8,[`loading`])]),J.value?(c(),E(`p`,ve,[o[44]||=C(` 宿主机安装 cron:`,-1),A(`code`,null,a(J.value.cron_install_command),1),o[45]||=C(` · 日志:`,-1),A(`code`,null,a(J.value.health_log_path),1)])):x(``,!0)]),_:1})]),_:1})]),_:1}),S(r,{cols:`12`,lg:`6`},{default:d(()=>[S(h,{elevation:`0`,border:``,rounded:`lg`,class:`mb-4`},{default:d(()=>[S(s,{class:`d-flex align-center`},{default:d(()=>[S(j,{class:`mr-2`},{default:d(()=>[...o[51]||=[C(`mdi-lock`,-1)]]),_:1}),o[52]||=C(` 修改密码 `,-1)]),_:1}),S(p),S(M,null,{default:d(()=>[S(k,{modelValue:Y.value.current,"onUpdate:modelValue":o[10]||=e=>Y.value.current=e,label:`当前密码`,variant:`outlined`,density:`compact`,type:`password`,class:`mb-3`,rules:[_(N)(`密码`)]},null,8,[`modelValue`,`rules`]),S(k,{modelValue:Y.value.new_pw,"onUpdate:modelValue":o[11]||=e=>Y.value.new_pw=e,label:`新密码`,variant:`outlined`,density:`compact`,type:`password`,class:`mb-3`,rules:[_(N)(`密码`)]},null,8,[`modelValue`,`rules`]),S(k,{modelValue:Y.value.confirm,"onUpdate:modelValue":o[12]||=e=>Y.value.confirm=e,label:`确认新密码`,variant:`outlined`,density:`compact`,type:`password`,class:`mb-3`,rules:[_(N)(`确认密码`),_(P)(()=>Y.value.new_pw)]},null,8,[`modelValue`,`rules`]),_(z).admin?.totp_enabled?(c(),b(k,{key:0,modelValue:Y.value.totp_code,"onUpdate:modelValue":o[13]||=e=>Y.value.totp_code=e,label:`TOTP 验证码`,variant:`outlined`,density:`compact`,type:`text`,inputmode:`numeric`,maxlength:`6`,class:`mb-3`,rules:_(z).admin?.totp_enabled?[_(N)(`验证码`)]:[]},null,8,[`modelValue`,`rules`])):x(``,!0)]),_:1}),S(u,{class:`px-4 pb-4`},{default:d(()=>[S(f),S(m,{color:`primary`,variant:`flat`,onClick:Dt,loading:Je.value},{default:d(()=>[...o[53]||=[C(`修改密码`,-1)]]),_:1},8,[`loading`])]),_:1})]),_:1}),S(h,{elevation:`0`,border:``,rounded:`lg`,class:`mb-4`},{default:d(()=>[S(s,{class:`d-flex align-center`},{default:d(()=>[S(j,{class:`mr-2`},{default:d(()=>[...o[54]||=[C(`mdi-shield-key`,-1)]]),_:1}),o[55]||=C(` 双因素认证 (TOTP) `,-1)]),_:1}),S(p),S(M,null,{default:d(()=>[A(`div`,ye,[o[56]||=A(`span`,{class:`text-body-2`},`状态:`,-1),S(l,{color:_(z).admin?.totp_enabled?`success`:`grey`,size:`small`,variant:`tonal`,label:``,class:`ml-2`},{default:d(()=>[C(a(_(z).admin?.totp_enabled?`已启用`:`未启用`),1)]),_:1},8,[`color`])]),Ye.value?(c(),E(`div`,be,[A(`img`,{src:Ye.value,alt:`TOTP QR`,style:{"max-width":`200px`}},null,8,xe),Xe.value?(c(),E(`div`,Se,[A(`code`,Ce,a(Xe.value),1),S(m,{size:`x-small`,variant:`text`,icon:`mdi-content-copy`,onClick:Mt})])):x(``,!0),o[57]||=A(`div`,{class:`text-caption text-medium-emphasis mt-1`},`用认证器 App 扫描二维码或手动输入密钥`,-1)])):x(``,!0),_(z).admin?.totp_enabled?(c(),b(m,{key:2,size:`small`,variant:`tonal`,color:`error`,"prepend-icon":`mdi-shield-off`,onClick:At},{default:d(()=>[...o[60]||=[C(`禁用 TOTP`,-1)]]),_:1})):(c(),E(`div`,we,[Ye.value?(c(),E(`div`,Te,[S(k,{modelValue:Ze.value,"onUpdate:modelValue":o[14]||=e=>Ze.value=e,label:`验证码`,variant:`outlined`,density:`compact`,style:{"max-width":`160px`}},null,8,[`modelValue`]),S(m,{color:`primary`,variant:`flat`,size:`small`,onClick:kt,loading:Qe.value},{default:d(()=>[...o[59]||=[C(`启用`,-1)]]),_:1},8,[`loading`])])):(c(),b(m,{key:0,size:`small`,variant:`tonal`,"prepend-icon":`mdi-qrcode`,onClick:Ot},{default:d(()=>[...o[58]||=[C(`生成二维码`,-1)]]),_:1}))]))]),_:1})]),_:1}),S(h,{elevation:`0`,border:``,rounded:`lg`,class:`mb-4`},{default:d(()=>[S(s,{class:`d-flex align-center`},{default:d(()=>[S(j,{class:`mr-2`},{default:d(()=>[...o[61]||=[C(`mdi-key`,-1)]]),_:1}),o[62]||=C(` API Key `,-1)]),_:1}),S(p),S(M,null,{default:d(()=>[A(`div`,Ee,[S(k,{"model-value":X.value?rt.value:`••••••••`,label:`API Key`,variant:`outlined`,density:`compact`,readonly:``,type:X.value?`text`:`password`,style:{"max-width":`320px`}},null,8,[`model-value`,`type`]),S(m,{variant:`text`,size:`small`,icon:X.value?`mdi-eye-off`:`mdi-eye`,onClick:Nt,loading:it.value},null,8,[`icon`,`loading`]),S(m,{variant:`text`,size:`small`,icon:`mdi-content-copy`,onClick:Ft})])]),_:1})]),_:1}),S(h,{elevation:`0`,border:``,rounded:`lg`},{default:d(()=>[S(s,{class:`d-flex align-center`},{default:d(()=>[S(j,{class:`mr-2`},{default:d(()=>[...o[63]||=[C(`mdi-ip-network`,-1)]]),_:1}),o[64]||=C(` 登录 IP 白名单 `,-1)]),_:1}),S(p),S(M,null,{default:d(()=>[S(R,{modelValue:ot.value,"onUpdate:modelValue":[o[15]||=e=>ot.value=e,It],label:`启用 IP 白名单(仅白名单 IP 可登录)`,color:`primary`,density:`compact`,"hide-details":``,class:`mb-4`},null,8,[`modelValue`]),o[67]||=A(`div`,{class:`text-subtitle-2 mb-2`},`代理订阅`,-1),o[68]||=A(`p`,{class:`text-caption text-medium-emphasis mb-2`},` 填写 SS/VMess/VLESS 等订阅链接,保存后每 2 小时自动刷新节点 IP 并合并进白名单。 `,-1),S(k,{modelValue:Z.value,"onUpdate:modelValue":o[16]||=e=>Z.value=e,label:`订阅 URL`,variant:`outlined`,density:`compact`,class:`mb-2`,placeholder:`https://example.com/sub?token=...`},null,8,[`modelValue`]),A(`div`,De,[S(m,{size:`small`,variant:`tonal`,"prepend-icon":`mdi-cloud-download`,onClick:Lt,loading:dt.value},{default:d(()=>[...o[65]||=[C(`解析预览`,-1)]]),_:1},8,[`loading`]),S(m,{size:`small`,variant:`tonal`,color:`primary`,"prepend-icon":`mdi-content-save`,onClick:Rt,loading:ut.value},{default:d(()=>[...o[66]||=[C(`保存白名单`,-1)]]),_:1},8,[`loading`])]),Q.value.length?(c(),E(`div`,Oe,[A(`div`,ke,[C(` 订阅节点 `+a(Q.value.length)+` 个 `,1),ct.value?(c(),E(`span`,Ae,` · 上次刷新 `+a(ct.value),1)):x(``,!0)]),A(`div`,je,[(c(!0),E(O,null,T(pt.value,e=>(c(),b(l,{key:e,size:`x-small`,variant:`tonal`,label:``},{default:d(()=>[C(a(e),1)]),_:2},1024))),128)),Q.value.length>pt.value.length?(c(),b(l,{key:0,size:`x-small`,variant:`text`,label:``},{default:d(()=>[C(` +`+a(Q.value.length-pt.value.length)+` 更多 `,1)]),_:1})):x(``,!0)])])):Z.value?(c(),E(`div`,Me,a(ct.value?`上次刷新 ${ct.value},暂无节点`:`等待首次刷新…保存订阅 URL 后将自动拉取`),1)):x(``,!0),S(p,{class:`my-3`}),o[69]||=A(`div`,{class:`text-subtitle-2 mb-2`},`手动 IP`,-1),S(le,{modelValue:st.value,"onUpdate:modelValue":o[17]||=e=>st.value=e,label:`手动 IP / CIDR(每行一个)`,variant:`outlined`,density:`compact`,rows:`4`,class:`mb-2`,placeholder:`1.2.3.4
|
||
10.0.0.0/24`},null,8,[`modelValue`]),A(`div`,Ne,` 手动 IP 与订阅节点合并生效;共 `+a(lt.value)+` 条(去重后)。 `,1)]),_:1})]),_:1})]),_:1})]),_:1}),S(L,{modelValue:$.value,"onUpdate:modelValue":o[20]||=e=>$.value=e,"max-width":`520`},{default:d(()=>[S(h,{border:``},{default:d(()=>[S(s,null,{default:d(()=>[...o[70]||=[C(`订阅解析预览`,-1)]]),_:1}),S(M,null,{default:d(()=>[A(`div`,Pe,[o[71]||=C(`共解析出 `,-1),A(`strong`,null,a(ft.value.length),1),o[72]||=C(` 个节点 IP / 域名:`,-1)]),A(`div`,Fe,[(c(!0),E(O,null,T(ft.value,e=>(c(),b(l,{key:e,size:`small`,variant:`tonal`,label:``},{default:d(()=>[C(a(e),1)]),_:2},1024))),128))]),o[73]||=A(`div`,{class:`text-caption text-medium-emphasis`},`点击「保存白名单」后才会写入订阅 URL 并触发后台刷新。`,-1)]),_:1}),S(u,null,{default:d(()=>[S(f),S(m,{variant:`text`,onClick:o[18]||=e=>$.value=!1},{default:d(()=>[...o[74]||=[C(`关闭`,-1)]]),_:1}),S(m,{color:`primary`,variant:`flat`,onClick:o[19]||=e=>{$.value=!1,Rt()}},{default:d(()=>[...o[75]||=[C(`保存白名单`,-1)]]),_:1})]),_:1})]),_:1})]),_:1},8,[`modelValue`]),S(L,{modelValue:G.value,"onUpdate:modelValue":o[22]||=e=>G.value=e,"max-width":`480`},{default:d(()=>[S(h,{border:``},{default:d(()=>[S(s,null,{default:d(()=>[...o[76]||=[C(`选择 Chat ID`,-1)]]),_:1}),S(M,null,{default:d(()=>[Ve.value.length?(c(),b(ie,{key:1,density:`compact`,lines:`two`},{default:d(()=>[(c(!0),E(O,null,T(Ve.value,e=>(c(),b(ee,{key:e.id,title:e.title||e.username||`Chat ${e.id}`,subtitle:`${e.type}${e.username?` · @`+e.username:``} · ID ${e.id}`,onClick:t=>Et(e)},null,8,[`title`,`subtitle`,`onClick`]))),128))]),_:1})):(c(),E(`div`,Ie,` 未检测到对话。请先在 Telegram 向 Bot 发送一条消息,然后重新检测。 `))]),_:1}),S(u,null,{default:d(()=>[S(f),S(m,{variant:`text`,onClick:o[21]||=e=>G.value=!1},{default:d(()=>[...o[77]||=[C(`关闭`,-1)]]),_:1})]),_:1})]),_:1})]),_:1},8,[`modelValue`]),S(L,{modelValue:$e.value,"onUpdate:modelValue":o[26]||=e=>$e.value=e,"max-width":`400`},{default:d(()=>[S(h,{border:``},{default:d(()=>[S(s,null,{default:d(()=>[...o[78]||=[C(`禁用 TOTP`,-1)]]),_:1}),S(M,null,{default:d(()=>[S(k,{modelValue:et.value,"onUpdate:modelValue":o[23]||=e=>et.value=e,label:`当前密码`,variant:`outlined`,density:`compact`,type:`password`,class:`mb-2`,rules:[_(N)()]},null,8,[`modelValue`,`rules`]),S(k,{modelValue:tt.value,"onUpdate:modelValue":o[24]||=e=>tt.value=e,label:`验证码`,variant:`outlined`,density:`compact`,rules:[_(N)()]},null,8,[`modelValue`,`rules`])]),_:1}),S(u,null,{default:d(()=>[S(f),S(m,{variant:`text`,onClick:o[25]||=e=>$e.value=!1},{default:d(()=>[...o[79]||=[C(`取消`,-1)]]),_:1}),S(m,{color:`error`,variant:`flat`,onClick:jt,loading:nt.value},{default:d(()=>[...o[80]||=[C(`禁用`,-1)]]),_:1},8,[`loading`])]),_:1})]),_:1})]),_:1},8,[`modelValue`])]),_:1}))}});export{Le as default}; |