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; }