From 64902b124acc4832ac7d6402033385c59ddd9e5c Mon Sep 17 00:00:00 2001 From: JOJO <1498581755@qq.com> Date: Sat, 25 Jul 2026 16:52:42 +0800 Subject: [PATCH] =?UTF-8?q?feat(admin):=20=E4=BA=8C=E7=BA=A7=E5=AF=86?= =?UTF-8?q?=E7=A0=81=E6=9C=AA=E9=85=8D=E7=BD=AE=E6=97=B6=E6=98=BE=E7=A4=BA?= =?UTF-8?q?=E8=AE=BE=E7=BD=AE=E5=BC=95=E5=AF=BC=E9=A1=B5=EF=BC=8C=E7=99=BB?= =?UTF-8?q?=E5=BD=95=20Docker=20=E6=8A=A5=E9=94=99=E6=94=B9=E4=B8=BA?= =?UTF-8?q?=E5=8E=9F=E5=9C=B0=E5=BC=B9=E7=AA=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/user_container_manager.py | 4 + server/admin.py | 10 +- static/admin_api/index.html | 2 + static/admin_dashboard/index.html | 2 + static/admin_policy/index.html | 2 + static/custom_tools/index.html | 2 + static/src/admin/AdminDashboardApp.vue | 104 ++----------- static/src/admin/ApiAdminApp.vue | 103 ++---------- static/src/admin/CustomToolsApp.vue | 104 ++----------- static/src/admin/PolicyApp.vue | 108 ++----------- static/src/admin/SecondaryGate.vue | 208 +++++++++++++++++++++++++ static/src/admin/useSecondaryPass.ts | 5 + static/src/auth/LoginApp.vue | 8 +- 13 files changed, 292 insertions(+), 370 deletions(-) create mode 100644 static/src/admin/SecondaryGate.vue diff --git a/modules/user_container_manager.py b/modules/user_container_manager.py index 2b3270c1..64b21bcf 100644 --- a/modules/user_container_manager.py +++ b/modules/user_container_manager.py @@ -293,6 +293,10 @@ class UserContainerManager: if not self.image: raise RuntimeError("TERMINAL_SANDBOX_IMAGE 未配置,无法启动容器。") + # 创建前先做 daemon 就绪检查:daemon 未启动时给出友好中文报错, + # 而不是 docker run 的英文原始 stderr(如 Cannot connect to the Docker daemon...) + self._assert_docker_runtime_ready() + container_name = self._build_container_name(username) self._kill_container(container_name, docker_path) diff --git a/server/admin.py b/server/admin.py index 42d7c134..0e09678e 100644 --- a/server/admin.py +++ b/server/admin.py @@ -102,7 +102,15 @@ def admin_api_page(): @login_required @admin_required def admin_secondary_status(): - return jsonify({"success": True, "verified": _is_secondary_verified(), "ttl": ADMIN_SECONDARY_TTL_SECONDS}) + # configured:是否已设置二级密码(hash 或明文)。未配置时前端应显示设置引导, + # 而不是密码输入框(未配置时 verify 恒 401,输入什么都进不去)。 + configured = bool(ADMIN_SECONDARY_PASSWORD_HASH or ADMIN_SECONDARY_PASSWORD) + return jsonify({ + "success": True, + "verified": _is_secondary_verified(), + "configured": configured, + "ttl": ADMIN_SECONDARY_TTL_SECONDS, + }) @admin_bp.route('/api/admin/secondary/verify', methods=['POST']) diff --git a/static/admin_api/index.html b/static/admin_api/index.html index 626ca036..7f863f30 100644 --- a/static/admin_api/index.html +++ b/static/admin_api/index.html @@ -5,6 +5,8 @@ API 管理面板 + + diff --git a/static/admin_dashboard/index.html b/static/admin_dashboard/index.html index 2ef5efc0..70ade6f4 100644 --- a/static/admin_dashboard/index.html +++ b/static/admin_dashboard/index.html @@ -9,6 +9,8 @@ + + diff --git a/static/admin_policy/index.html b/static/admin_policy/index.html index 1f752657..2c133b65 100644 --- a/static/admin_policy/index.html +++ b/static/admin_policy/index.html @@ -9,6 +9,8 @@ + + diff --git a/static/custom_tools/index.html b/static/custom_tools/index.html index 6262eb27..a81291c5 100644 --- a/static/custom_tools/index.html +++ b/static/custom_tools/index.html @@ -9,6 +9,8 @@ + + diff --git a/static/src/admin/AdminDashboardApp.vue b/static/src/admin/AdminDashboardApp.vue index e8244484..c29dcb59 100644 --- a/static/src/admin/AdminDashboardApp.vue +++ b/static/src/admin/AdminDashboardApp.vue @@ -4,34 +4,15 @@

正在加载监控数据...

-
-
-

请输入管理员二级密码

-

为保护敏感监控数据,需二级校验后查看。

- -
- - -
-

{{ secondaryError }}

-
-
+

管理员监控面板

@@ -426,6 +407,7 @@ + + + + + diff --git a/static/src/admin/useSecondaryPass.ts b/static/src/admin/useSecondaryPass.ts index 171b273f..2174a33b 100644 --- a/static/src/admin/useSecondaryPass.ts +++ b/static/src/admin/useSecondaryPass.ts @@ -2,6 +2,8 @@ import { ref } from 'vue'; export function useSecondaryPass() { const verified = ref(false); + // 是否已在服务端配置二级密码;未配置时前端应显示设置引导而非密码输入框 + const configured = ref(true); const loading = ref(false); const error = ref(null); @@ -13,6 +15,8 @@ export function useSecondaryPass() { if (!resp.ok) throw new Error(`状态请求失败:${resp.status}`); const data = await resp.json(); verified.value = !!data.verified; + // 兼容旧后端:字段缺失时视为已配置(保持原有输入框行为) + configured.value = data.configured !== false; } catch (err: any) { error.value = err.message || '无法验证二级密码状态'; } finally { @@ -45,6 +49,7 @@ export function useSecondaryPass() { return { verified, + configured, loading, error, check, diff --git a/static/src/auth/LoginApp.vue b/static/src/auth/LoginApp.vue index d5bb4c5e..422f10cb 100644 --- a/static/src/auth/LoginApp.vue +++ b/static/src/auth/LoginApp.vue @@ -77,8 +77,10 @@ const doLogin = async (redirectUrl = '/') => { body: JSON.stringify({ email: email.value, password: password.value }) }); + // 503 = ensure_container 失败(Docker 未启动/镜像缺失等),error 带具体原因,原地显示 if (resp.status === 503) { - window.location.href = '/resource_busy'; + const data = await resp.json().catch(() => ({} as any)); + error.value = data.error || '服务暂时不可用,请稍后重试'; return; } @@ -104,8 +106,10 @@ const doHostLogin = async (redirectUrl = '/') => { try { const resp = await fetch('/host-login', { method: 'POST' }); + // 503 = 容量满或终端创建失败,error 带具体原因,原地显示 if (resp.status === 503) { - window.location.href = '/resource_busy'; + const data = await resp.json().catch(() => ({} as any)); + error.value = data.error || '资源繁忙,请稍后再试'; return; }