From 6c6c88b22a562d47db728569a96595efa0cb386e Mon Sep 17 00:00:00 2001 From: qinglong Date: Sun, 14 Jun 2026 20:14:22 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E6=8F=92=E4=BB=B6API=E8=B7=AF=E7=94=B1?= =?UTF-8?q?=E7=BB=9F=E4=B8=80=E5=8A=A0=E9=9D=A2=E6=9D=BF=E5=89=8D=E7=BC=80?= =?UTF-8?q?(/SenSu)=EF=BC=8C=E8=A7=A3=E5=86=B3=E5=8F=8D=E4=BB=A3=E5=90=8E?= =?UTF-8?q?=E6=8F=92=E4=BB=B6=E9=A1=B5=E9=9D=A2=E7=A9=BA=E7=99=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- plugins/sentinel/dashboard.html | 10 +++++----- services/internet_service.py | 3 ++- services/web_panel/manager.py | 4 +++- services/web_panel/routes/plugin_web.py | 12 +++++++----- static/web_panel/js/app.js | 11 +++++++++-- 5 files changed, 26 insertions(+), 14 deletions(-) diff --git a/plugins/sentinel/dashboard.html b/plugins/sentinel/dashboard.html index 1b67bf4..0d6ea27 100644 --- a/plugins/sentinel/dashboard.html +++ b/plugins/sentinel/dashboard.html @@ -527,7 +527,7 @@ function bar(v) { return v < 60 ? 'bar-green' : (v < 85 ? 'bar-yellow' : 'bar-re function esc(s) { return String(s).replace(/&/g,'&').replace(//g,'>').replace(/"/g,'"'); } function pollNodes() { - fetch("/sentinel/api/nodes").then(function(r){return r.json()}).then(function(d){ + fetch((window.SENSU_BASE||"")+"/sentinel/api/nodes").then(function(r){return r.json()}).then(function(d){ renderNodes(d.nodes || []); }).catch(function(e){ var grid = document.getElementById("sentinel-grid"); @@ -579,7 +579,7 @@ function bar(v) { return v < 60 ? 'bar-green' : (v < 85 ? 'bar-yellow' : 'bar-re notes: document.getElementById("edit-notes").value.trim() }; if(!data.name||!data.url){ alert("名称和 URL 不能为空"); return; } - var resp = await fetch("/sentinel/api/nodes",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(data)}); + var resp = await fetch((window.SENSU_BASE||"")+"/sentinel/api/nodes",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(data)}); var r = await resp.json(); if(r.ok){ hideEdit(); loadNodes(); } else alert("保存失败: "+(r.error||"")); @@ -587,7 +587,7 @@ function bar(v) { return v < 60 ? 'bar-green' : (v < 85 ? 'bar-yellow' : 'bar-re async function deleteNode(id,name){ if(!confirm("确认删除节点 '"+name+"'?")) return; - await fetch("/sentinel/api/nodes/delete",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({id:id})}); + await fetch((window.SENSU_BASE||"")+"/sentinel/api/nodes/delete",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({id:id})}); loadNodes(); } @@ -597,7 +597,7 @@ function bar(v) { return v < 60 ? 'bar-green' : (v < 85 ? 'bar-yellow' : 'bar-re var el = document.getElementById("test-result"); el.innerHTML = '⏳ 测试中...'; try { - var resp = await fetch("/sentinel/api/nodes/test",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({url:url,api_key:key})}); + var resp = await fetch((window.SENSU_BASE||"")+"/sentinel/api/nodes/test",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({url:url,api_key:key})}); var r = await resp.json(); if(r.ok) el.innerHTML = '✅ 连接成功 — CPU:'+(r.system.cpu.percent||0)+'% MEM:'+(r.system.memory.percent||0)+'%'; else el.innerHTML = '❌ '+esc(r.error||'失败')+''; @@ -605,7 +605,7 @@ function bar(v) { return v < 60 ? 'bar-green' : (v < 85 ? 'bar-yellow' : 'bar-re } async function loadNodes(){ - var resp = await fetch("/sentinel/api/nodes"); + var resp = await fetch((window.SENSU_BASE||"")+"/sentinel/api/nodes"); var data = await resp.json(); var nodes = data.nodes||[]; var list = document.getElementById("node-list"); diff --git a/services/internet_service.py b/services/internet_service.py index c41acec..91434bd 100644 --- a/services/internet_service.py +++ b/services/internet_service.py @@ -202,7 +202,8 @@ class InternetService: if not route_path.startswith('/'): route_path = '/' + route_path - full_path = f"/{plugin_name}{route_path}" + base = self.http_app.get('panel_base_path', '') or '' + full_path = f"{base}/{plugin_name}{route_path}" # 创建包装器处理权限验证 async def wrapped_handler(request): diff --git a/services/web_panel/manager.py b/services/web_panel/manager.py index 540fce5..156a320 100644 --- a/services/web_panel/manager.py +++ b/services/web_panel/manager.py @@ -85,7 +85,9 @@ class WebPanelManager: logs.setup_routes(app, self.base_path) projects.setup_project_routes(app, self.sm, self.base_path) proxy.setup_proxy_routes(app, self.sm, self.base_path) - plugin_web.setup_plugin_web_routes(app, self.sm) + # 存储面板前缀供插件路由使用 + app['panel_base_path'] = self.base_path + plugin_web.setup_plugin_web_routes(app, self.sm, self.base_path) files.setup_file_routes(app, self.sm, self.base_path) # 注册日志广播 diff --git a/services/web_panel/routes/plugin_web.py b/services/web_panel/routes/plugin_web.py index 119f405..379911e 100644 --- a/services/web_panel/routes/plugin_web.py +++ b/services/web_panel/routes/plugin_web.py @@ -4,7 +4,9 @@ from ..utils.auth import panel_auth logger = logging.getLogger(__name__) -def setup_plugin_web_routes(app, service_manager): +def setup_plugin_web_routes(app, service_manager, base_path=""): + prefix = base_path.rstrip("/") + async def plugin_page(request): name = request.match_info.get("name","") ps = service_manager.get_service("plugin") @@ -36,7 +38,7 @@ def setup_plugin_web_routes(app, service_manager): return web.json_response({"ok": True}) return web.json_response({"ok": False}, status=404) - app.router.add_get("/plugin/{name}", panel_auth(plugin_page)) - app.router.add_get("/plugin/{name}/sse", panel_auth(plugin_sse)) - app.router.add_post("/plugin/{name}/event", panel_auth(plugin_event)) - logger.info("Plugin web routes registered (已加认证)") + app.router.add_get(f"{prefix}/plugin/{{name}}", panel_auth(plugin_page)) + app.router.add_get(f"{prefix}/plugin/{{name}}/sse", panel_auth(plugin_sse)) + app.router.add_post(f"{prefix}/plugin/{{name}}/event", panel_auth(plugin_event)) + logger.info(f"Plugin web routes registered at {prefix}/plugin/* (已加认证)") diff --git a/static/web_panel/js/app.js b/static/web_panel/js/app.js index 08594cd..deccb87 100644 --- a/static/web_panel/js/app.js +++ b/static/web_panel/js/app.js @@ -44,7 +44,14 @@ window.toggleTheme = function(){ // ═══ 路由系统 ═══ // Hash format: #/dashboard | #/files | #/plugins/example_plugin // Built-in pages map to ./static/pages/{name}.html -// Plugin pages load from /plugin/{name} +// Plugin pages load from {base}/plugin/{name} + +// 从当前页面路径提取面板前缀 (e.g. /SenSu/home.html → /SenSu) +window.SENSU_BASE = (function(){ + var p = window.location.pathname; + // 去掉最后的文件名部分 + return p.substring(0, p.lastIndexOf('/')) || ''; +})(); var BUILTIN_PAGES = ['dashboard','logs','console','plugins','projects','proxy','files','settings']; var _pluginPagesCache = null; // {plugin_name: {path, title, icon}} @@ -167,7 +174,7 @@ async function loadPluginPage(pluginName) { if(!_pluginPagesCache) await fetchPluginPages(); try { - var resp = await fetch('/plugin/'+pluginName+'?t='+Date.now(), {credentials:'include'}); + var resp = await fetch(window.SENSU_BASE+'/plugin/'+pluginName+'?t='+Date.now(), {credentials:'include'}); if(!resp.ok) throw new Error(resp.status); var html = await resp.text();