perf(runtime): switch socketio to eventlet and optimize asset chunk caching

This commit is contained in:
2026-02-07 16:09:21 +08:00
parent 9d1d4d701e
commit 43f1867033
36 changed files with 228 additions and 124 deletions

View File

@@ -13,7 +13,18 @@ export default defineConfig({
rollupOptions: {
output: {
manualChunks(id) {
if (id.includes('node_modules')) return 'vendor'
if (!id.includes('node_modules')) return undefined
if (id.includes('/node_modules/vue/') || id.includes('/node_modules/@vue/') || id.includes('/node_modules/vue-router/')) {
return 'vendor-vue'
}
if (id.includes('/node_modules/element-plus/') || id.includes('/node_modules/@element-plus/')) {
return 'vendor-element'
}
if (id.includes('/node_modules/axios/')) {
return 'vendor-axios'
}
return 'vendor-misc'
},
},
},

54
app.py
View File

@@ -14,6 +14,7 @@ from __future__ import annotations
import atexit
import os
import re
import signal
import sys
import threading
@@ -86,6 +87,23 @@ if not app.config.get("SECRET_KEY"):
cors_origins = os.environ.get("CORS_ALLOWED_ORIGINS", "").strip()
cors_allowed = [o.strip() for o in cors_origins.split(",") if o.strip()] if cors_origins else []
_socketio_preferred_mode = (os.environ.get("SOCKETIO_ASYNC_MODE", "eventlet") or "").strip().lower()
if _socketio_preferred_mode in {"", "auto"}:
_socketio_preferred_mode = None
_socketio_fallback_reason = None
try:
socketio = SocketIO(
app,
cors_allowed_origins=cors_allowed if cors_allowed else None,
async_mode=_socketio_preferred_mode,
ping_timeout=60,
ping_interval=25,
logger=False,
engineio_logger=False,
)
except Exception as socketio_error:
_socketio_fallback_reason = str(socketio_error)
socketio = SocketIO(
app,
cors_allowed_origins=cors_allowed if cors_allowed else None,
@@ -98,6 +116,9 @@ socketio = SocketIO(
init_logging(log_level=config.LOG_LEVEL, log_file=config.LOG_FILE)
logger = get_logger("app")
if _socketio_fallback_reason:
logger.warning(f"[SocketIO] 初始化失败,已回退 threading 模式: {_socketio_fallback_reason}")
logger.info(f"[SocketIO] 当前 async_mode: {socketio.async_mode}")
init_runtime(socketio=socketio, logger=logger)
_API_DIAGNOSTIC_LOG = str(os.environ.get("API_DIAGNOSTIC_LOG", "0")).strip().lower() in {
@@ -114,6 +135,9 @@ def _is_api_or_health_path(path: str) -> bool:
return raw.startswith("/api/") or raw.startswith("/yuyx/api/") or raw == "/health"
_HASHED_STATIC_ASSET_RE = re.compile(r".*-[a-z0-9_-]{8,}\.(?:js|css|woff2?|ttf|svg|png|jpe?g|webp)$", re.IGNORECASE)
# 初始化安全中间件(需在其他中间件/Blueprint 之前注册)
init_security_middleware(app)
@@ -226,10 +250,15 @@ def serve_static(filename):
if not is_safe_path("static", filename):
return jsonify({"error": "非法路径"}), 403
cache_ttl = 3600
lowered = filename.lower()
if "/assets/" in lowered or lowered.endswith((".js", ".css", ".woff", ".woff2", ".ttf", ".svg")):
is_asset_file = "/assets/" in lowered or lowered.endswith((".js", ".css", ".woff", ".woff2", ".ttf", ".svg"))
is_hashed_asset = bool(_HASHED_STATIC_ASSET_RE.match(lowered))
cache_ttl = 3600
if is_asset_file:
cache_ttl = 604800 # 7天
if is_hashed_asset:
cache_ttl = 31536000 # 365天
if request.args.get("v"):
cache_ttl = max(cache_ttl, 604800)
@@ -247,6 +276,11 @@ def serve_static(filename):
pass
response.headers.setdefault("Vary", "Accept-Encoding")
if is_hashed_asset:
response.headers["Cache-Control"] = f"public, max-age={cache_ttl}, immutable"
elif is_asset_file:
response.headers["Cache-Control"] = f"public, max-age={cache_ttl}, stale-while-revalidate=60"
else:
response.headers["Cache-Control"] = f"public, max-age={cache_ttl}"
return response
@@ -416,10 +450,12 @@ if __name__ == "__main__":
_init_screenshot_worker_pool()
_warmup_api_connection()
socketio.run(
app,
host=config.SERVER_HOST,
port=config.SERVER_PORT,
debug=config.DEBUG,
allow_unsafe_werkzeug=True,
)
run_kwargs = {
"host": config.SERVER_HOST,
"port": config.SERVER_PORT,
"debug": config.DEBUG,
}
if str(socketio.async_mode) == "threading":
run_kwargs["allow_unsafe_werkzeug"] = True
socketio.run(app, **run_kwargs)

View File

@@ -26,6 +26,7 @@ services:
# Flask 配置
- FLASK_ENV=production
- FLASK_DEBUG=false
- SOCKETIO_ASYNC_MODE=eventlet
# 服务器配置
- SERVER_HOST=0.0.0.0
- SERVER_PORT=51233

View File

@@ -12,3 +12,4 @@ beautifulsoup4==4.12.2
cryptography>=41.0.0
Pillow>=10.0.0
playwright==1.42.0
eventlet==0.36.1

View File

@@ -1,10 +1,10 @@
{
"_MetricGrid-BVtiO-YF.js": {
"file": "assets/MetricGrid-BVtiO-YF.js",
"_MetricGrid-Ccuhy_ft.js": {
"file": "assets/MetricGrid-Ccuhy_ft.js",
"name": "MetricGrid",
"imports": [
"index.html",
"_vendor-BczUEOE_.js"
"_vendor-vue-CVxSw_oJ.js"
],
"css": [
"assets/MetricGrid-yP_dkP6X.css"
@@ -14,52 +14,74 @@
"file": "assets/MetricGrid-yP_dkP6X.css",
"src": "_MetricGrid-yP_dkP6X.css"
},
"_email-DAYJAuoX.js": {
"file": "assets/email-DAYJAuoX.js",
"_email-CM3Ck82y.js": {
"file": "assets/email-CM3Ck82y.js",
"name": "email",
"imports": [
"index.html"
]
},
"_system-DL-3JJNY.js": {
"file": "assets/system-DL-3JJNY.js",
"_system-BtiUc9P2.js": {
"file": "assets/system-BtiUc9P2.js",
"name": "system",
"imports": [
"index.html"
]
},
"_tasks-myrNC0AE.js": {
"file": "assets/tasks-myrNC0AE.js",
"_tasks-BfRZ9z-p.js": {
"file": "assets/tasks-BfRZ9z-p.js",
"name": "tasks",
"imports": [
"index.html"
]
},
"_users-D6PiqKCG.js": {
"file": "assets/users-D6PiqKCG.js",
"_users-D6tusdlB.js": {
"file": "assets/users-D6tusdlB.js",
"name": "users",
"imports": [
"index.html"
]
},
"_vendor-BczUEOE_.js": {
"file": "assets/vendor-BczUEOE_.js",
"name": "vendor",
"_vendor-axios-B9ygI19o.js": {
"file": "assets/vendor-axios-B9ygI19o.js",
"name": "vendor-axios"
},
"_vendor-element-B5S5pUKo.js": {
"file": "assets/vendor-element-B5S5pUKo.js",
"name": "vendor-element",
"imports": [
"_vendor-vue-CVxSw_oJ.js",
"_vendor-misc-BeoNyvBp.js"
],
"css": [
"assets/vendor-C68yOrAy.css"
"assets/vendor-element-C68yOrAy.css"
]
},
"_vendor-C68yOrAy.css": {
"file": "assets/vendor-C68yOrAy.css",
"src": "_vendor-C68yOrAy.css"
"_vendor-element-C68yOrAy.css": {
"file": "assets/vendor-element-C68yOrAy.css",
"src": "_vendor-element-C68yOrAy.css"
},
"_vendor-misc-BeoNyvBp.js": {
"file": "assets/vendor-misc-BeoNyvBp.js",
"name": "vendor-misc",
"imports": [
"_vendor-vue-CVxSw_oJ.js"
]
},
"_vendor-vue-CVxSw_oJ.js": {
"file": "assets/vendor-vue-CVxSw_oJ.js",
"name": "vendor-vue"
},
"index.html": {
"file": "assets/index-kL5eq844.js",
"file": "assets/index-DqW7qFKO.js",
"name": "index",
"src": "index.html",
"isEntry": true,
"imports": [
"_vendor-BczUEOE_.js"
"_vendor-vue-CVxSw_oJ.js",
"_vendor-element-B5S5pUKo.js",
"_vendor-axios-B9ygI19o.js",
"_vendor-misc-BeoNyvBp.js"
],
"dynamicImports": [
"src/pages/ReportPage.vue",
@@ -77,129 +99,156 @@
]
},
"src/pages/AnnouncementsPage.vue": {
"file": "assets/AnnouncementsPage-DYL1hMLj.js",
"file": "assets/AnnouncementsPage-4fRVykRR.js",
"name": "AnnouncementsPage",
"src": "src/pages/AnnouncementsPage.vue",
"isDynamicEntry": true,
"imports": [
"_vendor-BczUEOE_.js",
"index.html"
"_vendor-element-B5S5pUKo.js",
"index.html",
"_vendor-vue-CVxSw_oJ.js",
"_vendor-misc-BeoNyvBp.js",
"_vendor-axios-B9ygI19o.js"
],
"css": [
"assets/AnnouncementsPage-DOwZaaOu.css"
]
},
"src/pages/EmailPage.vue": {
"file": "assets/EmailPage-C4cAH9uz.js",
"file": "assets/EmailPage-PZGlUp9r.js",
"name": "EmailPage",
"src": "src/pages/EmailPage.vue",
"isDynamicEntry": true,
"imports": [
"_email-DAYJAuoX.js",
"_email-CM3Ck82y.js",
"index.html",
"_MetricGrid-BVtiO-YF.js",
"_vendor-BczUEOE_.js"
"_MetricGrid-Ccuhy_ft.js",
"_vendor-element-B5S5pUKo.js",
"_vendor-vue-CVxSw_oJ.js",
"_vendor-axios-B9ygI19o.js",
"_vendor-misc-BeoNyvBp.js"
],
"css": [
"assets/EmailPage-BmPCDPYC.css"
]
},
"src/pages/FeedbacksPage.vue": {
"file": "assets/FeedbacksPage-BNIBDNSN.js",
"file": "assets/FeedbacksPage-CtSxl4wH.js",
"name": "FeedbacksPage",
"src": "src/pages/FeedbacksPage.vue",
"isDynamicEntry": true,
"imports": [
"index.html",
"_MetricGrid-BVtiO-YF.js",
"_vendor-BczUEOE_.js"
"_MetricGrid-Ccuhy_ft.js",
"_vendor-element-B5S5pUKo.js",
"_vendor-vue-CVxSw_oJ.js",
"_vendor-axios-B9ygI19o.js",
"_vendor-misc-BeoNyvBp.js"
],
"css": [
"assets/FeedbacksPage-mrXjCiV2.css"
]
},
"src/pages/LogsPage.vue": {
"file": "assets/LogsPage-B869d840.js",
"file": "assets/LogsPage-CbKK04n3.js",
"name": "LogsPage",
"src": "src/pages/LogsPage.vue",
"isDynamicEntry": true,
"imports": [
"_users-D6PiqKCG.js",
"_tasks-myrNC0AE.js",
"_users-D6tusdlB.js",
"_tasks-BfRZ9z-p.js",
"index.html",
"_vendor-BczUEOE_.js"
"_vendor-element-B5S5pUKo.js",
"_vendor-vue-CVxSw_oJ.js",
"_vendor-axios-B9ygI19o.js",
"_vendor-misc-BeoNyvBp.js"
],
"css": [
"assets/LogsPage-D1bozCEo.css"
]
},
"src/pages/ReportPage.vue": {
"file": "assets/ReportPage-CfEp_Gyo.js",
"file": "assets/ReportPage-iNbHoKlg.js",
"name": "ReportPage",
"src": "src/pages/ReportPage.vue",
"isDynamicEntry": true,
"imports": [
"_vendor-BczUEOE_.js",
"_vendor-element-B5S5pUKo.js",
"index.html",
"_email-DAYJAuoX.js",
"_tasks-myrNC0AE.js",
"_system-DL-3JJNY.js",
"_MetricGrid-BVtiO-YF.js"
"_email-CM3Ck82y.js",
"_tasks-BfRZ9z-p.js",
"_system-BtiUc9P2.js",
"_MetricGrid-Ccuhy_ft.js",
"_vendor-vue-CVxSw_oJ.js",
"_vendor-misc-BeoNyvBp.js",
"_vendor-axios-B9ygI19o.js"
],
"css": [
"assets/ReportPage-DN5YlEZa.css"
]
},
"src/pages/SecurityPage.vue": {
"file": "assets/SecurityPage-CGNDFsTS.js",
"file": "assets/SecurityPage-B0McTZDe.js",
"name": "SecurityPage",
"src": "src/pages/SecurityPage.vue",
"isDynamicEntry": true,
"imports": [
"index.html",
"_MetricGrid-BVtiO-YF.js",
"_vendor-BczUEOE_.js"
"_MetricGrid-Ccuhy_ft.js",
"_vendor-element-B5S5pUKo.js",
"_vendor-vue-CVxSw_oJ.js",
"_vendor-axios-B9ygI19o.js",
"_vendor-misc-BeoNyvBp.js"
],
"css": [
"assets/SecurityPage-DN76ndc_.css"
]
},
"src/pages/SettingsPage.vue": {
"file": "assets/SettingsPage-i1tUy5L8.js",
"file": "assets/SettingsPage-BpxjhELo.js",
"name": "SettingsPage",
"src": "src/pages/SettingsPage.vue",
"isDynamicEntry": true,
"imports": [
"index.html",
"_vendor-BczUEOE_.js"
"_vendor-element-B5S5pUKo.js",
"_vendor-vue-CVxSw_oJ.js",
"_vendor-axios-B9ygI19o.js",
"_vendor-misc-BeoNyvBp.js"
],
"css": [
"assets/SettingsPage-DaB8PeRL.css"
]
},
"src/pages/SystemPage.vue": {
"file": "assets/SystemPage-CZDqSnbM.js",
"file": "assets/SystemPage-BkDUSPOh.js",
"name": "SystemPage",
"src": "src/pages/SystemPage.vue",
"isDynamicEntry": true,
"imports": [
"_system-DL-3JJNY.js",
"_system-BtiUc9P2.js",
"index.html",
"_vendor-BczUEOE_.js"
"_vendor-element-B5S5pUKo.js",
"_vendor-vue-CVxSw_oJ.js",
"_vendor-axios-B9ygI19o.js",
"_vendor-misc-BeoNyvBp.js"
],
"css": [
"assets/SystemPage-DYBocGi2.css"
]
},
"src/pages/UsersPage.vue": {
"file": "assets/UsersPage-C5VKxxv4.js",
"file": "assets/UsersPage-1H0TJ5-u.js",
"name": "UsersPage",
"src": "src/pages/UsersPage.vue",
"isDynamicEntry": true,
"imports": [
"_users-D6PiqKCG.js",
"_users-D6tusdlB.js",
"index.html",
"_vendor-BczUEOE_.js"
"_vendor-element-B5S5pUKo.js",
"_vendor-vue-CVxSw_oJ.js",
"_vendor-axios-B9ygI19o.js",
"_vendor-misc-BeoNyvBp.js"
],
"css": [
"assets/UsersPage-Cow_LicY.css"

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1 +0,0 @@
import{_}from"./index-kL5eq844.js";import{r as n,l as s,o as t,F as r,m as u,V as p,k as o,i as l,j as y,w as h,c as i,A as k,B as c,C as v,a0 as f}from"./vendor-BczUEOE_.js";const b={class:"metric-top"},x={key:0,class:"metric-icon"},B={class:"metric-label"},C={class:"metric-value"},g={key:0,class:"metric-hint app-muted"},N={__name:"MetricGrid",props:{items:{type:Array,default:()=>[]},loading:{type:Boolean,default:!1},minWidth:{type:Number,default:180}},setup(a){return(V,w)=>{const d=n("el-icon"),m=n("el-skeleton");return t(),s("div",{class:"metric-grid",style:f({"--metric-min":`${a.minWidth}px`})},[(t(!0),s(r,null,u(a.items,e=>(t(),s("div",{key:e?.key||e?.label,class:p(["metric-card",`metric-tone--${e?.tone||"blue"}`])},[o("div",b,[e?.icon?(t(),s("div",x,[y(d,null,{default:h(()=>[(t(),i(k(e.icon)))]),_:2},1024)])):l("",!0),o("div",B,c(e?.label||"-"),1)]),o("div",C,[a.loading?(t(),i(m,{key:0,rows:1,animated:""})):(t(),s(r,{key:1},[v(c(e?.value??0),1)],64))]),e?.hint||e?.sub?(t(),s("div",g,c(e?.hint||e?.sub),1)):l("",!0)],2))),128))],4)}}},A=_(N,[["__scopeId","data-v-00e217d4"]]);export{A as M};

View File

@@ -0,0 +1 @@
import{_}from"./index-DqW7qFKO.js";import{aj as c,n as s,q as t,K as r,a3 as u,y as p,t as o,G as l,L as y,E as h,D as i,H as v,J as n,I as k,x as f}from"./vendor-vue-CVxSw_oJ.js";const b={class:"metric-top"},x={key:0,class:"metric-icon"},g={class:"metric-label"},B={class:"metric-value"},C={key:0,class:"metric-hint app-muted"},N={__name:"MetricGrid",props:{items:{type:Array,default:()=>[]},loading:{type:Boolean,default:!1},minWidth:{type:Number,default:180}},setup(a){return(V,D)=>{const d=c("el-icon"),m=c("el-skeleton");return t(),s("div",{class:"metric-grid",style:f({"--metric-min":`${a.minWidth}px`})},[(t(!0),s(r,null,u(a.items,e=>(t(),s("div",{key:e?.key||e?.label,class:p(["metric-card",`metric-tone--${e?.tone||"blue"}`])},[o("div",b,[e?.icon?(t(),s("div",x,[y(d,null,{default:h(()=>[(t(),i(v(e.icon)))]),_:2},1024)])):l("",!0),o("div",g,n(e?.label||"-"),1)]),o("div",B,[a.loading?(t(),i(m,{key:0,rows:1,animated:""})):(t(),s(r,{key:1},[k(n(e?.value??0),1)],64))]),e?.hint||e?.sub?(t(),s("div",C,n(e?.hint||e?.sub),1)):l("",!0)],2))),128))],4)}}},w=_(N,[["__scopeId","data-v-00e217d4"]]);export{w as M};

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
import{a as m,_ as h}from"./index-DqW7qFKO.js";import{a as u,E as x}from"./vendor-element-B5S5pUKo.js";import{r as p,aj as i,n as T,q as E,t as r,L as a,E as o,I as b}from"./vendor-vue-CVxSw_oJ.js";import"./vendor-axios-B9ygI19o.js";import"./vendor-misc-BeoNyvBp.js";async function P(l){const{data:s}=await m.put("/admin/username",{new_username:l});return s}async function C(l){const{data:s}=await m.put("/admin/password",{new_password:l});return s}async function S(){const{data:l}=await m.post("/logout");return l}const U={class:"page-stack"},A={__name:"SettingsPage",setup(l){const s=p(""),d=p(""),n=p(!1);function k(t){const e=String(t||"");return e.length<8?{ok:!1,message:"密码长度至少8位"}:e.length>128?{ok:!1,message:"密码长度不能超过128个字符"}:!/[a-zA-Z]/.test(e)||!/\d/.test(e)?{ok:!1,message:"密码必须包含字母和数字"}:{ok:!0,message:""}}async function f(){try{await S()}catch{}finally{window.location.href="/yuyx"}}async function V(){const t=s.value.trim();if(!t){u.error("请输入新用户名");return}try{await x.confirm(`确定将管理员用户名修改为「${t}」吗?修改后需要重新登录。`,"修改用户名",{confirmButtonText:"确认修改",cancelButtonText:"取消",type:"warning"})}catch{return}n.value=!0;try{await P(t),u.success("用户名修改成功,请重新登录"),s.value="",setTimeout(f,1200)}catch{}finally{n.value=!1}}async function B(){const t=d.value;if(!t){u.error("请输入新密码");return}const e=k(t);if(!e.ok){u.error(e.message);return}try{await x.confirm("确定修改管理员密码吗?修改后需要重新登录。","修改密码",{confirmButtonText:"确认修改",cancelButtonText:"取消",type:"warning"})}catch{return}n.value=!0;try{await C(t),u.success("密码修改成功,请重新登录"),d.value="",setTimeout(f,1200)}catch{}finally{n.value=!1}}return(t,e)=>{const g=i("el-input"),v=i("el-form-item"),w=i("el-form"),y=i("el-button"),_=i("el-card");return E(),T("div",U,[e[7]||(e[7]=r("div",{class:"app-page-title"},[r("h2",null,"设置"),r("span",{class:"app-muted"},"管理员账号设置")],-1)),a(_,{shadow:"never","body-style":{padding:"16px"},class:"card"},{default:o(()=>[e[3]||(e[3]=r("h3",{class:"section-title"},"修改管理员用户名",-1)),a(w,{"label-width":"120px"},{default:o(()=>[a(v,{label:"新用户名"},{default:o(()=>[a(g,{modelValue:s.value,"onUpdate:modelValue":e[0]||(e[0]=c=>s.value=c),placeholder:"输入新用户名",disabled:n.value},null,8,["modelValue","disabled"])]),_:1})]),_:1}),a(y,{type:"primary",loading:n.value,onClick:V},{default:o(()=>[...e[2]||(e[2]=[b("保存用户名",-1)])]),_:1},8,["loading"])]),_:1}),a(_,{shadow:"never","body-style":{padding:"16px"},class:"card"},{default:o(()=>[e[5]||(e[5]=r("h3",{class:"section-title"},"修改管理员密码",-1)),a(w,{"label-width":"120px"},{default:o(()=>[a(v,{label:"新密码"},{default:o(()=>[a(g,{modelValue:d.value,"onUpdate:modelValue":e[1]||(e[1]=c=>d.value=c),type:"password","show-password":"",placeholder:"输入新密码",disabled:n.value},null,8,["modelValue","disabled"])]),_:1})]),_:1}),a(y,{type:"primary",loading:n.value,onClick:B},{default:o(()=>[...e[4]||(e[4]=[b("保存密码",-1)])]),_:1},8,["loading"]),e[6]||(e[6]=r("div",{class:"help"},"建议使用更强密码至少8位且包含字母与数字。",-1))]),_:1})])}}},z=h(A,[["__scopeId","data-v-83d3840a"]]);export{z as default};

View File

@@ -1 +0,0 @@
import{a as m,_ as h}from"./index-kL5eq844.js";import{e as p,r as u,l as T,o as C,k as r,j as a,w as o,C as x,b as i,E as b}from"./vendor-BczUEOE_.js";async function P(l){const{data:s}=await m.put("/admin/username",{new_username:l});return s}async function E(l){const{data:s}=await m.put("/admin/password",{new_password:l});return s}async function S(){const{data:l}=await m.post("/logout");return l}const U={class:"page-stack"},A={__name:"SettingsPage",setup(l){const s=p(""),d=p(""),n=p(!1);function k(t){const e=String(t||"");return e.length<8?{ok:!1,message:"密码长度至少8位"}:e.length>128?{ok:!1,message:"密码长度不能超过128个字符"}:!/[a-zA-Z]/.test(e)||!/\d/.test(e)?{ok:!1,message:"密码必须包含字母和数字"}:{ok:!0,message:""}}async function f(){try{await S()}catch{}finally{window.location.href="/yuyx"}}async function V(){const t=s.value.trim();if(!t){i.error("请输入新用户名");return}try{await b.confirm(`确定将管理员用户名修改为「${t}」吗?修改后需要重新登录。`,"修改用户名",{confirmButtonText:"确认修改",cancelButtonText:"取消",type:"warning"})}catch{return}n.value=!0;try{await P(t),i.success("用户名修改成功,请重新登录"),s.value="",setTimeout(f,1200)}catch{}finally{n.value=!1}}async function B(){const t=d.value;if(!t){i.error("请输入新密码");return}const e=k(t);if(!e.ok){i.error(e.message);return}try{await b.confirm("确定修改管理员密码吗?修改后需要重新登录。","修改密码",{confirmButtonText:"确认修改",cancelButtonText:"取消",type:"warning"})}catch{return}n.value=!0;try{await E(t),i.success("密码修改成功,请重新登录"),d.value="",setTimeout(f,1200)}catch{}finally{n.value=!1}}return(t,e)=>{const g=u("el-input"),w=u("el-form-item"),v=u("el-form"),y=u("el-button"),_=u("el-card");return C(),T("div",U,[e[7]||(e[7]=r("div",{class:"app-page-title"},[r("h2",null,"设置"),r("span",{class:"app-muted"},"管理员账号设置")],-1)),a(_,{shadow:"never","body-style":{padding:"16px"},class:"card"},{default:o(()=>[e[3]||(e[3]=r("h3",{class:"section-title"},"修改管理员用户名",-1)),a(v,{"label-width":"120px"},{default:o(()=>[a(w,{label:"新用户名"},{default:o(()=>[a(g,{modelValue:s.value,"onUpdate:modelValue":e[0]||(e[0]=c=>s.value=c),placeholder:"输入新用户名",disabled:n.value},null,8,["modelValue","disabled"])]),_:1})]),_:1}),a(y,{type:"primary",loading:n.value,onClick:V},{default:o(()=>[...e[2]||(e[2]=[x("保存用户名",-1)])]),_:1},8,["loading"])]),_:1}),a(_,{shadow:"never","body-style":{padding:"16px"},class:"card"},{default:o(()=>[e[5]||(e[5]=r("h3",{class:"section-title"},"修改管理员密码",-1)),a(v,{"label-width":"120px"},{default:o(()=>[a(w,{label:"新密码"},{default:o(()=>[a(g,{modelValue:d.value,"onUpdate:modelValue":e[1]||(e[1]=c=>d.value=c),type:"password","show-password":"",placeholder:"输入新密码",disabled:n.value},null,8,["modelValue","disabled"])]),_:1})]),_:1}),a(y,{type:"primary",loading:n.value,onClick:B},{default:o(()=>[...e[4]||(e[4]=[x("保存密码",-1)])]),_:1},8,["loading"]),e[6]||(e[6]=r("div",{class:"help"},"建议使用更强密码至少8位且包含字母与数字。",-1))]),_:1})])}}},j=h(A,[["__scopeId","data-v-83d3840a"]]);export{j as default};

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1 +1 @@
import{a as n}from"./index-kL5eq844.js";async function i(){const{data:a}=await n.get("/email/settings");return a}async function e(a){const{data:t}=await n.post("/email/settings",a);return t}async function c(){const{data:a}=await n.get("/email/stats");return a}async function o(a){const{data:t}=await n.get("/email/logs",{params:a});return t}async function l(a){const{data:t}=await n.post("/email/logs/cleanup",{days:a});return t}export{o as a,i as b,l as c,c as f,e as u};
import{a as n}from"./index-DqW7qFKO.js";async function i(){const{data:a}=await n.get("/email/settings");return a}async function e(a){const{data:t}=await n.post("/email/settings",a);return t}async function c(){const{data:a}=await n.get("/email/stats");return a}async function o(a){const{data:t}=await n.get("/email/logs",{params:a});return t}async function l(a){const{data:t}=await n.post("/email/logs/cleanup",{days:a});return t}export{o as a,i as b,l as c,c as f,e as u};

File diff suppressed because one or more lines are too long

View File

@@ -1 +1 @@
import{a}from"./index-kL5eq844.js";async function o(){const{data:t}=await a.get("/system/config");return t}async function e(t){const{data:n}=await a.post("/system/config",t);return n}export{o as f,e as u};
import{a}from"./index-DqW7qFKO.js";async function o(){const{data:t}=await a.get("/system/config");return t}async function e(t){const{data:n}=await a.post("/system/config",t);return n}export{o as f,e as u};

View File

@@ -1 +1 @@
import{a}from"./index-kL5eq844.js";async function c(){const{data:t}=await a.get("/server/info");return t}async function e(){const{data:t}=await a.get("/docker_stats");return t}async function r(){const{data:t}=await a.get("/request_metrics");return t}async function o(){const{data:t}=await a.get("/slow_sql_metrics");return t}async function i(){const{data:t}=await a.get("/task/stats");return t}async function u(){const{data:t}=await a.get("/task/running");return t}async function f(t){const{data:s}=await a.get("/task/logs",{params:t});return s}async function g(t){const{data:s}=await a.post("/task/logs/clear",{days:t});return s}export{u as a,c as b,e as c,r as d,o as e,i as f,f as g,g as h};
import{a}from"./index-DqW7qFKO.js";async function c(){const{data:t}=await a.get("/server/info");return t}async function e(){const{data:t}=await a.get("/docker_stats");return t}async function r(){const{data:t}=await a.get("/request_metrics");return t}async function o(){const{data:t}=await a.get("/slow_sql_metrics");return t}async function i(){const{data:t}=await a.get("/task/stats");return t}async function u(){const{data:t}=await a.get("/task/running");return t}async function f(t){const{data:s}=await a.get("/task/logs",{params:t});return s}async function g(t){const{data:s}=await a.post("/task/logs/clear",{days:t});return s}export{u as a,c as b,e as c,r as d,o as e,i as f,f as g,g as h};

View File

@@ -1 +1 @@
import{a as t}from"./index-kL5eq844.js";async function n(){const{data:s}=await t.get("/users");return s}async function o(s){const{data:a}=await t.post(`/users/${s}/approve`);return a}async function c(s){const{data:a}=await t.post(`/users/${s}/reject`);return a}async function i(s){const{data:a}=await t.delete(`/users/${s}`);return a}async function u(s,a){const{data:e}=await t.post(`/users/${s}/vip`,{days:a});return e}async function p(s){const{data:a}=await t.delete(`/users/${s}/vip`);return a}async function d(s,a){const{data:e}=await t.post(`/users/${s}/reset_password`,{new_password:a});return e}export{o as a,p as b,d as c,i as d,n as f,c as r,u as s};
import{a as t}from"./index-DqW7qFKO.js";async function n(){const{data:s}=await t.get("/users");return s}async function o(s){const{data:a}=await t.post(`/users/${s}/approve`);return a}async function c(s){const{data:a}=await t.post(`/users/${s}/reject`);return a}async function i(s){const{data:a}=await t.delete(`/users/${s}`);return a}async function u(s,a){const{data:e}=await t.post(`/users/${s}/vip`,{days:a});return e}async function p(s){const{data:a}=await t.delete(`/users/${s}/vip`);return a}async function d(s,a){const{data:e}=await t.post(`/users/${s}/reset_password`,{new_password:a});return e}export{o as a,p as b,d as c,i as d,n as f,c as r,u as s};

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -5,9 +5,12 @@
<link rel="icon" type="image/svg+xml" href="./vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>后台管理 - 知识管理平台</title>
<script type="module" crossorigin src="./assets/index-kL5eq844.js"></script>
<link rel="modulepreload" crossorigin href="./assets/vendor-BczUEOE_.js">
<link rel="stylesheet" crossorigin href="./assets/vendor-C68yOrAy.css">
<script type="module" crossorigin src="./assets/index-DqW7qFKO.js"></script>
<link rel="modulepreload" crossorigin href="./assets/vendor-vue-CVxSw_oJ.js">
<link rel="modulepreload" crossorigin href="./assets/vendor-misc-BeoNyvBp.js">
<link rel="modulepreload" crossorigin href="./assets/vendor-element-B5S5pUKo.js">
<link rel="modulepreload" crossorigin href="./assets/vendor-axios-B9ygI19o.js">
<link rel="stylesheet" crossorigin href="./assets/vendor-element-C68yOrAy.css">
<link rel="stylesheet" crossorigin href="./assets/index-a3a11Ghn.css">
</head>
<body>