Skip to content

Commit ead90cb

Browse files
committed
feat: 添加 WebUI 静态资源包提示和相关错误处理逻辑
1 parent 56d1071 commit ead90cb

7 files changed

Lines changed: 51 additions & 274 deletions

File tree

locales/en-US/startup.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,13 +80,15 @@
8080
"startup.webui_auto_build_tool_missing": "❌ No supported frontend build tool was found, cannot auto-build WebUI",
8181
"startup.webui_cors_configured": "✅ CORS middleware configured",
8282
"startup.webui_dashboard_source_missing": "❌ WebUI frontend source directory not found: {dashboard_root}",
83+
"startup.webui_dashboard_package_hint": "💡 Install the prebuilt WebUI static asset package first, for example: {command}",
8384
"startup.webui_disabled": "WebUI is disabled",
8485
"startup.webui_index_missing": "❌ index.html not found: {index_path}",
8586
"startup.webui_manual_build_hint": "💡 Automatic recovery could not restore the frontend assets. Install dependencies and build manually in the dashboard directory: {command}",
8687
"startup.webui_path_traversal_detected": "🚫 Suspicious path traversal request detected: {full_path}",
8788
"startup.webui_robots_route_register_failed": "❌ Failed to register robots.txt route: {error}",
8889
"startup.webui_robots_route_registered": "✅ robots.txt route registered",
8990
"startup.webui_server_init_failed": "Failed to initialize WebUI server: {error}",
91+
"startup.webui_static_assets_unavailable": "❌ No usable WebUI static assets were found (installed maibot-dashboard package or local dashboard/dist)",
9092
"startup.webui_static_assets_try_auto_build": "⚠️ WebUI static assets are unavailable, attempting to auto-build the frontend...",
9193
"startup.webui_static_dir_missing": "❌ WebUI static directory does not exist",
9294
"startup.webui_static_dir_missing_with_path": "❌ WebUI static directory does not exist: {static_path}",

locales/ja-JP/startup.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,13 +80,15 @@
8080
"startup.webui_auto_build_tool_missing": "❌ 利用可能なフロントエンドビルドツールが見つからず、WebUI を自動ビルドできません",
8181
"startup.webui_cors_configured": "✅ CORS ミドルウェアを設定しました",
8282
"startup.webui_dashboard_source_missing": "❌ WebUI フロントエンドのソースディレクトリが見つかりません: {dashboard_root}",
83+
"startup.webui_dashboard_package_hint": "💡 事前ビルド済みの WebUI 静的リソースパッケージを先にインストールしてください。例: {command}",
8384
"startup.webui_disabled": "WebUI は無効です",
8485
"startup.webui_index_missing": "❌ index.html が見つかりません: {index_path}",
8586
"startup.webui_manual_build_hint": "💡 自動復旧でフロントエンド資産を復旧できなかったため、dashboard ディレクトリで依存関係をインストールして手動ビルドしてください: {command}",
8687
"startup.webui_path_traversal_detected": "🚫 パストラバーサルの疑いがあるリクエストを検出しました: {full_path}",
8788
"startup.webui_robots_route_register_failed": "❌ robots.txt ルートの登録に失敗しました: {error}",
8889
"startup.webui_robots_route_registered": "✅ robots.txt ルートを登録しました",
8990
"startup.webui_server_init_failed": "WebUI サーバーの初期化に失敗しました: {error}",
91+
"startup.webui_static_assets_unavailable": "❌ 利用可能な WebUI 静的アセットが見つかりません(インストール済みの maibot-dashboard パッケージまたはローカルの dashboard/dist)",
9092
"startup.webui_static_assets_try_auto_build": "⚠️ WebUI の静的アセットが利用できないため、フロントエンドの自動ビルドを試行します...",
9193
"startup.webui_static_dir_missing": "❌ WebUI の静的ディレクトリが存在しません",
9294
"startup.webui_static_dir_missing_with_path": "❌ WebUI の静的ディレクトリが存在しません: {static_path}",

locales/ko/startup.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,13 +80,15 @@
8080
"startup.webui_auto_build_tool_missing": "❌ 사용 가능한 프론트엔드 빌드 도구를 찾을 수 없어 WebUI를 자동으로 빌드할 수 없습니다",
8181
"startup.webui_cors_configured": "✅ CORS 미들웨어 설정 완료",
8282
"startup.webui_dashboard_source_missing": "❌ WebUI 프론트엔드 소스 디렉터리를 찾을 수 없습니다: {dashboard_root}",
83+
"startup.webui_dashboard_package_hint": "💡 미리 빌드된 WebUI 정적 리소스 패키지를 먼저 설치하세요. 예: {command}",
8384
"startup.webui_disabled": "WebUI가 비활성화되었습니다",
8485
"startup.webui_index_missing": "❌ index.html을 찾을 수 없습니다: {index_path}",
8586
"startup.webui_manual_build_hint": "💡 자동 복구로 프론트엔드 리소스를 수정하지 못했습니다. dashboard 디렉터리에서 의존성을 설치하고 수동으로 빌드하세요: {command}",
8687
"startup.webui_path_traversal_detected": "🚫 경로 탐색 공격이 의심되는 요청 감지됨: {full_path}",
8788
"startup.webui_robots_route_register_failed": "❌ robots.txt 라우트 등록 실패: {error}",
8889
"startup.webui_robots_route_registered": "✅ robots.txt 라우트 등록 완료",
8990
"startup.webui_server_init_failed": "WebUI 서버 초기화 실패: {error}",
91+
"startup.webui_static_assets_unavailable": "❌ 사용할 수 있는 WebUI 정적 리소스를 찾을 수 없습니다(설치된 maibot-dashboard 패키지 또는 로컬 dashboard/dist)",
9092
"startup.webui_static_assets_try_auto_build": "⚠️ WebUI 정적 리소스를 사용할 수 없어 프론트엔드 자동 빌드를 시도합니다...",
9193
"startup.webui_static_dir_missing": "❌ WebUI 정적 파일 디렉터리가 존재하지 않습니다",
9294
"startup.webui_static_dir_missing_with_path": "❌ WebUI 정적 파일 디렉터리가 존재하지 않습니다: {static_path}",

locales/zh-CN/startup.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,13 +80,15 @@
8080
"startup.webui_auto_build_tool_missing": "❌ 未找到可用的前端构建工具,无法自动构建 WebUI",
8181
"startup.webui_cors_configured": "✅ CORS 中间件已配置",
8282
"startup.webui_dashboard_source_missing": "❌ 未找到 WebUI 前端源码目录: {dashboard_root}",
83+
"startup.webui_dashboard_package_hint": "💡 请安装预构建的 WebUI 静态资源包,例如: {command}",
8384
"startup.webui_disabled": "WebUI 已禁用",
8485
"startup.webui_index_missing": "❌ 未找到 index.html: {index_path}",
8586
"startup.webui_manual_build_hint": "💡 自动恢复未能修复前端资源,请在 dashboard 目录安装依赖并手动构建: {command}",
8687
"startup.webui_path_traversal_detected": "🚫 检测到疑似路径穿越请求: {full_path}",
8788
"startup.webui_robots_route_register_failed": "❌ 注册 robots.txt 路由失败: {error}",
8889
"startup.webui_robots_route_registered": "✅ robots.txt 路由已注册",
8990
"startup.webui_server_init_failed": "初始化 WebUI 服务器失败: {error}",
91+
"startup.webui_static_assets_unavailable": "❌ 未找到可用的 WebUI 静态资源(已安装的 maibot-dashboard 包或本地 dashboard/dist)",
9092
"startup.webui_static_assets_try_auto_build": "⚠️ WebUI 静态资源不可用,尝试自动构建前端...",
9193
"startup.webui_static_dir_missing": "❌ WebUI 静态文件目录不存在",
9294
"startup.webui_static_dir_missing_with_path": "❌ WebUI 静态文件目录不存在: {static_path}",

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ dependencies = [
1919
"jieba>=0.42.1",
2020
"json-repair>=0.47.6",
2121
"maim-message>=0.6.2",
22+
"maibot-dashboard==1.0.0.dev2026040439",
2223
"maibot-plugin-sdk>=2.3.0",
2324
"mcp",
2425
"msgpack>=1.1.2",

pytests/webui/test_app.py

Lines changed: 30 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from pathlib import Path
12
from unittest.mock import patch
23

34
import pytest
@@ -10,54 +11,42 @@ def test_ensure_static_path_ready_uses_existing_static_path(tmp_path) -> None:
1011
static_path.mkdir()
1112
(static_path / "index.html").write_text("<html></html>", encoding="utf-8")
1213

13-
with (
14-
patch.object(webui_app, "_resolve_static_path", return_value=static_path),
15-
patch.object(webui_app, "_try_build_dashboard") as build_mock,
16-
):
14+
with patch.object(webui_app, "_resolve_static_path", return_value=static_path):
1715
result = webui_app._ensure_static_path_ready()
1816

1917
assert result == static_path
20-
build_mock.assert_not_called()
21-
2218

23-
def test_ensure_static_path_ready_retries_after_auto_build(tmp_path) -> None:
24-
static_path = tmp_path / "dist"
25-
static_path.mkdir()
26-
(static_path / "index.html").write_text("<html></html>", encoding="utf-8")
2719

20+
def test_ensure_static_path_ready_logs_install_hint_when_static_assets_are_missing() -> None:
2821
with (
29-
patch.object(webui_app, "_resolve_static_path", side_effect=[None, static_path]),
30-
patch.object(
31-
webui_app,
32-
"_try_build_dashboard",
33-
return_value=webui_app.DashboardAutoRecoveryResult(succeeded=True),
34-
) as build_mock,
22+
patch.object(webui_app, "_resolve_static_path", return_value=None),
23+
patch.object(webui_app.logger, "warning") as warning_mock,
3524
):
3625
result = webui_app._ensure_static_path_ready()
3726

38-
assert result == static_path
39-
build_mock.assert_called_once_with()
27+
assert result is None
28+
warning_mock.assert_any_call(webui_app.t("startup.webui_static_assets_unavailable"))
29+
warning_mock.assert_any_call(
30+
webui_app.t("startup.webui_dashboard_package_hint", command=webui_app._MANUAL_INSTALL_COMMAND)
31+
)
4032

4133

42-
def test_ensure_static_path_ready_logs_manual_hint_when_auto_build_fails() -> None:
34+
def test_ensure_static_path_ready_logs_index_error_when_static_path_is_invalid(tmp_path) -> None:
35+
static_path = tmp_path / "dist"
36+
static_path.mkdir()
37+
4338
with (
44-
patch.object(webui_app, "_resolve_static_path", return_value=None),
45-
patch.object(
46-
webui_app,
47-
"_try_build_dashboard",
48-
return_value=webui_app.DashboardAutoRecoveryResult(
49-
succeeded=False,
50-
manual_recovery_command=webui_app._MANUAL_BUILD_COMMAND,
51-
),
52-
),
39+
patch.object(webui_app, "_resolve_static_path", return_value=static_path),
5340
patch.object(webui_app.logger, "warning") as warning_mock,
5441
):
5542
result = webui_app._ensure_static_path_ready()
5643

5744
assert result is None
58-
warning_mock.assert_any_call(webui_app.t("startup.webui_auto_recovery_failed"))
5945
warning_mock.assert_any_call(
60-
webui_app.t("startup.webui_manual_build_hint", command=webui_app._MANUAL_BUILD_COMMAND)
46+
webui_app.t("startup.webui_index_missing", index_path=static_path / "index.html")
47+
)
48+
warning_mock.assert_any_call(
49+
webui_app.t("startup.webui_dashboard_package_hint", command=webui_app._MANUAL_INSTALL_COMMAND)
6150
)
6251

6352

@@ -73,43 +62,21 @@ def test_setup_static_files_does_not_duplicate_warning_when_static_path_is_unava
7362
warning_mock.assert_not_called()
7463

7564

76-
def test_get_dashboard_build_command_defaults_to_npm(tmp_path) -> None:
77-
(tmp_path / "package.json").write_text("{}", encoding="utf-8")
65+
def test_resolve_static_path_prefers_installed_dashboard_package(monkeypatch, tmp_path) -> None:
66+
package_dist = tmp_path / "site-packages" / "maibot_dashboard" / "dist"
67+
package_dist.mkdir(parents=True)
7868

79-
with patch.object(
80-
webui_app.shutil,
81-
"which",
82-
side_effect=lambda tool_name: "/usr/bin/npm" if tool_name == "npm" else None,
83-
):
84-
command = webui_app._get_dashboard_build_command(tmp_path)
69+
class _DashboardModule:
70+
@staticmethod
71+
def get_dist_path() -> Path:
72+
return package_dist
8573

86-
assert command == ["npm", "run", "build"]
87-
88-
89-
def test_try_build_dashboard_installs_missing_dependencies_before_build(monkeypatch, tmp_path) -> None:
90-
(tmp_path / "package.json").write_text("{}", encoding="utf-8")
91-
run_results = [
92-
webui_app.CompletedProcess(args=["npm", "install", "--no-package-lock"], returncode=0, stdout="", stderr=""),
93-
webui_app.CompletedProcess(args=["npm", "run", "build"], returncode=0, stdout="", stderr=""),
94-
]
74+
monkeypatch.setattr(webui_app, "_get_project_root", lambda: tmp_path)
9575

96-
monkeypatch.setattr(webui_app, "_get_dashboard_root", lambda: tmp_path)
97-
monkeypatch.setattr(webui_app, "_should_auto_install_dashboard_dependencies", lambda dashboard_root: True)
76+
with patch.object(webui_app, "import_module", return_value=_DashboardModule()):
77+
resolved_path = webui_app._resolve_static_path()
9878

99-
with (
100-
patch.object(webui_app, "_get_dashboard_build_command", return_value=["npm", "run", "build"]),
101-
patch.object(webui_app, "run", side_effect=run_results) as run_mock,
102-
):
103-
result = webui_app._try_build_dashboard()
104-
105-
assert result.succeeded is True
106-
assert run_mock.call_count == 2
107-
install_call = run_mock.call_args_list[0]
108-
build_call = run_mock.call_args_list[1]
109-
assert install_call.args[0] == ["npm", "install", "--no-package-lock"]
110-
assert install_call.kwargs["cwd"] == tmp_path
111-
assert build_call.args[0] == ["npm", "run", "build"]
112-
assert build_call.kwargs["cwd"] == tmp_path
79+
assert resolved_path == package_dist
11380

11481

11582
def test_resolve_static_path_uses_dashboard_dist(monkeypatch, tmp_path) -> None:

0 commit comments

Comments
 (0)