From 9815818def9bd28f5f3bf9664c4ef8f539f3a463 Mon Sep 17 00:00:00 2001 From: foxhui Date: Sun, 25 Jan 2026 03:59:16 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20WebUI=20=E6=94=AF=E6=8C=81=E4=BF=AE?= =?UTF-8?q?=E6=94=B9=E6=97=A5=E5=BF=97=E7=AD=89=E7=BA=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 4 ++++ src/config/manager.js | 4 +++- src/config/validator.js | 7 +++++++ webui/dist/assets/server.css | 2 +- webui/dist/assets/server.js | 2 +- webui/src/components/settings/server.vue | 17 +++++++++++++++++ 6 files changed, 33 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2edab1a..82375e2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [3.5.1] - 2026-01-24 +### ✨ Added +- **WebUI** + - 增加日志等级的设置 + ### 🐛 Fixed - **代理** - 修复无需鉴权的 Socks5 代理无法使用的问题 diff --git a/src/config/manager.js b/src/config/manager.js index f563eba..5f6bae1 100644 --- a/src/config/manager.js +++ b/src/config/manager.js @@ -46,7 +46,8 @@ export function getServerConfig() { return { port: config.server?.port || 3000, authToken: config.server?.auth || '', - keepaliveMode: config.server?.keepalive?.mode || 'comment' + keepaliveMode: config.server?.keepalive?.mode || 'comment', + logLevel: config.logLevel || 'info' }; } @@ -65,6 +66,7 @@ export function saveServerConfig(data) { if (!config.server.keepalive) config.server.keepalive = {}; config.server.keepalive.mode = data.keepaliveMode; } + if (data.logLevel !== undefined) config.logLevel = data.logLevel; writeConfig(config); } diff --git a/src/config/validator.js b/src/config/validator.js index 55c395c..da06c4e 100644 --- a/src/config/validator.js +++ b/src/config/validator.js @@ -38,6 +38,13 @@ export function validateServerConfig(data) { } } + // Log Level 校验 + if (data.logLevel !== undefined) { + if (!['debug', 'info', 'warn', 'error'].includes(data.logLevel)) { + errors.push('logLevel 必须是 debug、info、warn 或 error'); + } + } + // Queue Buffer 校验 if (data.queueBuffer !== undefined) { if (typeof data.queueBuffer !== 'number' || !Number.isInteger(data.queueBuffer)) { diff --git a/webui/dist/assets/server.css b/webui/dist/assets/server.css index 371b746..b94c966 100644 --- a/webui/dist/assets/server.css +++ b/webui/dist/assets/server.css @@ -1 +1 @@ -.ant-input-number[data-v-5b571484]{width:100%} +.ant-input-number[data-v-e88c66ad]{width:100%} diff --git a/webui/dist/assets/server.js b/webui/dist/assets/server.js index 8510a5b..d3537ff 100644 --- a/webui/dist/assets/server.js +++ b/webui/dist/assets/server.js @@ -1 +1 @@ -import{_ as k,k as w,l as c,o as T,b as C,d as S,w as a,c as o,g as e,f as l,h as i,m as B,M}from"./index.js";const U={style:{"margin-bottom":"8px"}},z={style:{"margin-bottom":"8px"}},I={style:{"margin-bottom":"8px"}},j={style:{display:"flex","justify-content":"flex-end","margin-top":"24px"}},q={style:{"margin-bottom":"8px"}},L={style:{"margin-bottom":"8px"}},N={style:{display:"flex","justify-content":"flex-end","margin-top":"24px"}},V={__name:"server",setup(A){const d=w(),n=c({port:5173,authToken:"",keepaliveMode:"comment",queueBuffer:2,imageLimit:5});T(async()=>{await d.fetchServerConfig(),Object.assign(n,d.serverConfig)});const m=async()=>{await d.saveServerConfig(n)},u=async()=>{if(n.authToken&&n.authToken.length>0&&n.authToken.length<10){B.error("鉴权 Token 如果设置则必须至少 10 个字符,或留空");return}if(!n.authToken){M.confirm({title:"安全警告",content:"您正在将鉴权 Token 留空,这意味着 API 和 WebUI 将无需认证即可访问。请勿在公网环境中使用此配置!确定要继续吗?",okText:"确定留空",okType:"danger",cancelText:"取消",onOk:m});return}await m()};return(O,t)=>{const p=l("a-input-number"),r=l("a-col"),y=l("a-input-password"),f=l("a-select-option"),_=l("a-select"),v=l("a-row"),x=l("a-button"),g=l("a-card"),b=l("a-layout");return S(),C(b,{style:{background:"transparent"}},{default:a(()=>[o(g,{title:"服务器设置",bordered:!1,style:{width:"100%"}},{default:a(()=>[o(v,{gutter:[16,16]},{default:a(()=>[o(r,{xs:24,md:12},{default:a(()=>[e("div",U,[t[5]||(t[5]=e("div",{style:{"font-weight":"600","margin-bottom":"4px"}},"监听端口",-1)),t[6]||(t[6]=e("div",{style:{"font-size":"12px",color:"#8c8c8c","margin-bottom":"8px"}}," 设置服务器监听的端口号,默认为 5173 ",-1)),o(p,{value:n.port,"onUpdate:value":t[0]||(t[0]=s=>n.port=s),min:1,max:65535,placeholder:"请输入端口号",style:{width:"100%"}},null,8,["value"])])]),_:1}),o(r,{xs:24,md:12},{default:a(()=>[e("div",z,[t[7]||(t[7]=e("div",{style:{"font-weight":"600","margin-bottom":"4px"}},"鉴权 Token",-1)),t[8]||(t[8]=e("div",{style:{"font-size":"12px",color:"#8c8c8c","margin-bottom":"8px"}}," 用于 API 请求鉴权的密钥,留空则不启用鉴权 ",-1)),o(y,{value:n.authToken,"onUpdate:value":t[1]||(t[1]=s=>n.authToken=s),placeholder:"请输入 Token",type:"password"},null,8,["value"])])]),_:1}),o(r,{xs:24,md:12},{default:a(()=>[e("div",I,[t[11]||(t[11]=e("div",{style:{"font-weight":"600","margin-bottom":"4px"}},"心跳包类型",-1)),t[12]||(t[12]=e("div",{style:{"font-size":"12px",color:"#8c8c8c","margin-bottom":"8px"}}," 选择 SSE 流式响应的心跳包格式 ",-1)),o(_,{value:n.keepaliveMode,"onUpdate:value":t[2]||(t[2]=s=>n.keepaliveMode=s),style:{width:"100%"},placeholder:"请选择心跳包类型"},{default:a(()=>[o(f,{value:"comment"},{default:a(()=>[...t[9]||(t[9]=[i("Comment - 注释格式",-1)])]),_:1}),o(f,{value:"content"},{default:a(()=>[...t[10]||(t[10]=[i("Content - 内容格式",-1)])]),_:1})]),_:1},8,["value"])])]),_:1})]),_:1}),e("div",j,[o(x,{type:"primary",onClick:u},{default:a(()=>[...t[13]||(t[13]=[i(" 保存设置 ",-1)])]),_:1})])]),_:1}),o(g,{title:"队列设置",bordered:!1,style:{width:"100%","margin-top":"10px"}},{default:a(()=>[o(v,{gutter:[16,16]},{default:a(()=>[o(r,{xs:24,md:12},{default:a(()=>[e("div",q,[t[14]||(t[14]=e("div",{style:{"font-weight":"600","margin-bottom":"4px"}},"队列缓冲区大小",-1)),t[15]||(t[15]=e("div",{style:{"font-size":"12px",color:"#8c8c8c","margin-bottom":"8px"}},[i(" 非流式请求的额外排队数(设为 0 则不限制非流式请求数量)"),e("br"),i(" 实际队列上限 = Workers数量 + 缓冲区大小 ")],-1)),o(p,{value:n.queueBuffer,"onUpdate:value":t[3]||(t[3]=s=>n.queueBuffer=s),min:0,max:100,placeholder:"默认为 2",style:{width:"100%"}},null,8,["value"])])]),_:1}),o(r,{xs:24,md:12},{default:a(()=>[e("div",L,[t[16]||(t[16]=e("div",{style:{"font-weight":"600","margin-bottom":"4px"}},"图片数量上限",-1)),t[17]||(t[17]=e("div",{style:{"font-size":"12px",color:"#8c8c8c","margin-bottom":"8px"}},[i(" 单次请求最多支持的图片附件数量"),e("br"),i(" 网页最多支持10个附件,超出会被丢弃 ")],-1)),o(p,{value:n.imageLimit,"onUpdate:value":t[4]||(t[4]=s=>n.imageLimit=s),min:1,max:10,placeholder:"默认为 5",style:{width:"100%"}},null,8,["value"])])]),_:1})]),_:1}),e("div",N,[o(x,{type:"primary",onClick:u},{default:a(()=>[...t[18]||(t[18]=[i(" 保存设置 ",-1)])]),_:1})])]),_:1})]),_:1})}}},W=k(V,[["__scopeId","data-v-5b571484"]]);export{W as default}; +import{_ as k,k as w,l as T,o as C,b as S,d as U,w as n,c as e,g as o,f as i,h as a,m as z,M as B}from"./index.js";const L={style:{"margin-bottom":"8px"}},M={style:{"margin-bottom":"8px"}},I={style:{"margin-bottom":"8px"}},j={style:{"margin-bottom":"8px"}},q={style:{display:"flex","justify-content":"flex-end","margin-top":"24px"}},N={style:{"margin-bottom":"8px"}},V={style:{"margin-bottom":"8px"}},W={style:{display:"flex","justify-content":"flex-end","margin-top":"24px"}},A={__name:"server",setup(D){const u=w(),l=T({port:5173,authToken:"",keepaliveMode:"comment",logLevel:"info",queueBuffer:2,imageLimit:5});C(async()=>{await u.fetchServerConfig(),Object.assign(l,u.serverConfig)});const m=async()=>{await u.saveServerConfig(l)},f=async()=>{if(l.authToken&&l.authToken.length>0&&l.authToken.length<10){z.error("鉴权 Token 如果设置则必须至少 10 个字符,或留空");return}if(!l.authToken){B.confirm({title:"安全警告",content:"您正在将鉴权 Token 留空,这意味着 API 和 WebUI 将无需认证即可访问。请勿在公网环境中使用此配置!确定要继续吗?",okText:"确定留空",okType:"danger",cancelText:"取消",onOk:m});return}await m()};return(E,t)=>{const p=i("a-input-number"),r=i("a-col"),b=i("a-input-password"),d=i("a-select-option"),v=i("a-select"),g=i("a-row"),x=i("a-button"),y=i("a-card"),_=i("a-layout");return U(),S(_,{style:{background:"transparent"}},{default:n(()=>[e(y,{title:"服务器设置",bordered:!1,style:{width:"100%"}},{default:n(()=>[e(g,{gutter:[16,16]},{default:n(()=>[e(r,{xs:24,md:12},{default:n(()=>[o("div",L,[t[6]||(t[6]=o("div",{style:{"font-weight":"600","margin-bottom":"4px"}},"监听端口",-1)),t[7]||(t[7]=o("div",{style:{"font-size":"12px",color:"#8c8c8c","margin-bottom":"8px"}}," 设置服务器监听的端口号,默认为 5173 ",-1)),e(p,{value:l.port,"onUpdate:value":t[0]||(t[0]=s=>l.port=s),min:1,max:65535,placeholder:"请输入端口号",style:{width:"100%"}},null,8,["value"])])]),_:1}),e(r,{xs:24,md:12},{default:n(()=>[o("div",M,[t[8]||(t[8]=o("div",{style:{"font-weight":"600","margin-bottom":"4px"}},"鉴权 Token",-1)),t[9]||(t[9]=o("div",{style:{"font-size":"12px",color:"#8c8c8c","margin-bottom":"8px"}}," 用于 API 请求鉴权的密钥,留空则不启用鉴权 ",-1)),e(b,{value:l.authToken,"onUpdate:value":t[1]||(t[1]=s=>l.authToken=s),placeholder:"请输入 Token",type:"password"},null,8,["value"])])]),_:1}),e(r,{xs:24,md:12},{default:n(()=>[o("div",I,[t[12]||(t[12]=o("div",{style:{"font-weight":"600","margin-bottom":"4px"}},"心跳包类型",-1)),t[13]||(t[13]=o("div",{style:{"font-size":"12px",color:"#8c8c8c","margin-bottom":"8px"}}," 选择 SSE 流式响应的心跳包格式 ",-1)),e(v,{value:l.keepaliveMode,"onUpdate:value":t[2]||(t[2]=s=>l.keepaliveMode=s),style:{width:"100%"},placeholder:"请选择心跳包类型"},{default:n(()=>[e(d,{value:"comment"},{default:n(()=>[...t[10]||(t[10]=[a("Comment - 注释格式",-1)])]),_:1}),e(d,{value:"content"},{default:n(()=>[...t[11]||(t[11]=[a("Content - 内容格式",-1)])]),_:1})]),_:1},8,["value"])])]),_:1}),e(r,{xs:24,md:12},{default:n(()=>[o("div",j,[t[18]||(t[18]=o("div",{style:{"font-weight":"600","margin-bottom":"4px"}},"日志等级",-1)),t[19]||(t[19]=o("div",{style:{"font-size":"12px",color:"#8c8c8c","margin-bottom":"8px"}}," 设置服务器日志输出的详细程度 ",-1)),e(v,{value:l.logLevel,"onUpdate:value":t[3]||(t[3]=s=>l.logLevel=s),style:{width:"100%"},placeholder:"请选择日志等级"},{default:n(()=>[e(d,{value:"debug"},{default:n(()=>[...t[14]||(t[14]=[a("Debug - 调试日志",-1)])]),_:1}),e(d,{value:"info"},{default:n(()=>[...t[15]||(t[15]=[a("Info - 普通信息",-1)])]),_:1}),e(d,{value:"warn"},{default:n(()=>[...t[16]||(t[16]=[a("Warn - 警告信息",-1)])]),_:1}),e(d,{value:"error"},{default:n(()=>[...t[17]||(t[17]=[a("Error - 仅错误",-1)])]),_:1})]),_:1},8,["value"])])]),_:1})]),_:1}),o("div",q,[e(x,{type:"primary",onClick:f},{default:n(()=>[...t[20]||(t[20]=[a(" 保存设置 ",-1)])]),_:1})])]),_:1}),e(y,{title:"队列设置",bordered:!1,style:{width:"100%","margin-top":"10px"}},{default:n(()=>[e(g,{gutter:[16,16]},{default:n(()=>[e(r,{xs:24,md:12},{default:n(()=>[o("div",N,[t[21]||(t[21]=o("div",{style:{"font-weight":"600","margin-bottom":"4px"}},"队列缓冲区大小",-1)),t[22]||(t[22]=o("div",{style:{"font-size":"12px",color:"#8c8c8c","margin-bottom":"8px"}},[a(" 非流式请求的额外排队数(设为 0 则不限制非流式请求数量)"),o("br"),a(" 实际队列上限 = Workers数量 + 缓冲区大小 ")],-1)),e(p,{value:l.queueBuffer,"onUpdate:value":t[4]||(t[4]=s=>l.queueBuffer=s),min:0,max:100,placeholder:"默认为 2",style:{width:"100%"}},null,8,["value"])])]),_:1}),e(r,{xs:24,md:12},{default:n(()=>[o("div",V,[t[23]||(t[23]=o("div",{style:{"font-weight":"600","margin-bottom":"4px"}},"图片数量上限",-1)),t[24]||(t[24]=o("div",{style:{"font-size":"12px",color:"#8c8c8c","margin-bottom":"8px"}},[a(" 单次请求最多支持的图片附件数量"),o("br"),a(" 网页最多支持10个附件,超出会被丢弃 ")],-1)),e(p,{value:l.imageLimit,"onUpdate:value":t[5]||(t[5]=s=>l.imageLimit=s),min:1,max:10,placeholder:"默认为 5",style:{width:"100%"}},null,8,["value"])])]),_:1})]),_:1}),o("div",W,[e(x,{type:"primary",onClick:f},{default:n(()=>[...t[25]||(t[25]=[a(" 保存设置 ",-1)])]),_:1})])]),_:1})]),_:1})}}},P=k(A,[["__scopeId","data-v-e88c66ad"]]);export{P as default}; diff --git a/webui/src/components/settings/server.vue b/webui/src/components/settings/server.vue index 2371c32..3229e40 100644 --- a/webui/src/components/settings/server.vue +++ b/webui/src/components/settings/server.vue @@ -10,6 +10,7 @@ const formData = reactive({ port: 5173, authToken: '', keepaliveMode: 'comment', + logLevel: 'info', queueBuffer: 2, imageLimit: 5 }); @@ -91,6 +92,22 @@ const handleSave = async () => { + + + +
+
日志等级
+
+ 设置服务器日志输出的详细程度 +
+ + Debug - 调试日志 + Info - 普通信息 + Warn - 警告信息 + Error - 仅错误 + +
+