diff --git a/zhenxun/plugins/web_ui/__init__.py b/zhenxun/plugins/web_ui/__init__.py
index 35fcf9fe..d05e55f9 100644
--- a/zhenxun/plugins/web_ui/__init__.py
+++ b/zhenxun/plugins/web_ui/__init__.py
@@ -20,6 +20,7 @@ from .api.tabs.manage.chat import ws_router as chat_routes
from .api.tabs.plugin_manage import router as plugin_router
from .api.tabs.system import router as system_router
from .auth import router as auth_router
+from .public import init_public
__plugin_meta__ = PluginMetadata(
name="WebUi",
@@ -59,7 +60,7 @@ WsApiRouter.include_router(chat_routes)
@driver.on_startup
-def _():
+async def _():
try:
async def log_sink(message: str):
@@ -80,6 +81,7 @@ def _():
app: FastAPI = nonebot.get_app()
app.include_router(BaseApiRouter)
app.include_router(WsApiRouter)
+ await init_public(app)
logger.info("API启动成功", "Web UI")
except Exception as e:
logger.error("API启动失败", "Web UI", e=e)
diff --git a/zhenxun/plugins/web_ui/public/__init__.py b/zhenxun/plugins/web_ui/public/__init__.py
new file mode 100644
index 00000000..144dcda8
--- /dev/null
+++ b/zhenxun/plugins/web_ui/public/__init__.py
@@ -0,0 +1,35 @@
+from fastapi.responses import FileResponse
+from fastapi.staticfiles import StaticFiles
+from fastapi import APIRouter, FastAPI
+
+from zhenxun.services.log import logger
+
+from .config import PUBLIC_PATH
+from .data_source import update_webui_assets
+
+router = APIRouter()
+
+
+@router.get("/")
+async def index():
+ return FileResponse(PUBLIC_PATH / "index.html")
+
+
+@router.get("/favicon.ico")
+async def favicon():
+ return FileResponse(PUBLIC_PATH / "favicon.ico")
+
+
+async def init_public(app: FastAPI):
+ try:
+ if not PUBLIC_PATH.exists():
+ await update_webui_assets()
+ app.include_router(router)
+ for pathname in ["css", "js", "fonts", "img"]:
+ app.mount(
+ f"/{pathname}",
+ StaticFiles(directory=PUBLIC_PATH / pathname, check_dir=True),
+ name=f"public_{pathname}",
+ )
+ except Exception as e:
+ logger.error(f"初始化 web ui assets 失败 e: {e}", "Web UI assets")
\ No newline at end of file
diff --git a/zhenxun/plugins/web_ui/public/config.py b/zhenxun/plugins/web_ui/public/config.py
new file mode 100644
index 00000000..7c27d38d
--- /dev/null
+++ b/zhenxun/plugins/web_ui/public/config.py
@@ -0,0 +1,20 @@
+from datetime import datetime
+from pydantic import BaseModel
+from zhenxun.configs.path_config import DATA_PATH, TEMP_PATH
+
+
+class PublicData(BaseModel):
+ etag: str
+ update_time: datetime
+
+
+COMMAND_NAME = "webui_update_assets"
+
+WEBUI_DATA_PATH = DATA_PATH / "web_ui"
+PUBLIC_PATH = WEBUI_DATA_PATH / "public"
+TMP_PATH = TEMP_PATH / "web_ui"
+
+GITHUB_API_COMMITS = "https://api.github.com/repos/HibiKier/zhenxun_bot_webui/commits"
+WEBUI_ASSETS_DOWNLOAD_URL = (
+ "https://github.com/HibiKier/zhenxun_bot_webui/archive/refs/heads/dist.zip"
+)
diff --git a/zhenxun/plugins/web_ui/public/data_source.py b/zhenxun/plugins/web_ui/public/data_source.py
new file mode 100644
index 00000000..65fe5d3c
--- /dev/null
+++ b/zhenxun/plugins/web_ui/public/data_source.py
@@ -0,0 +1,50 @@
+import os
+import shutil
+import zipfile
+
+from pathlib import Path
+from nonebot.utils import run_sync
+from zhenxun.services.log import logger
+from zhenxun.utils.http_utils import AsyncHttpx
+
+from .config import (
+ WEBUI_ASSETS_DOWNLOAD_URL,
+ WEBUI_DATA_PATH,
+ TMP_PATH,
+ COMMAND_NAME,
+ PUBLIC_PATH,
+)
+
+
+async def update_webui_assets():
+ webui_assets_path = TMP_PATH / "webui_assets.zip"
+ if await AsyncHttpx.download_file(
+ WEBUI_ASSETS_DOWNLOAD_URL, webui_assets_path, follow_redirects=True
+ ):
+ logger.info("下载 webui_assets 成功...", COMMAND_NAME)
+ else:
+ logger.error("下载 webui_assets 失败...", COMMAND_NAME)
+
+ await _file_handle(webui_assets_path)
+
+ logger.info("更新 webui_assets 成功...", COMMAND_NAME)
+ return True
+
+
+@run_sync
+def _file_handle(webui_assets_path: Path):
+ logger.debug("开始解压 webui_assets...", COMMAND_NAME)
+ if webui_assets_path.exists():
+ tf = zipfile.ZipFile(webui_assets_path)
+ tf.extractall(TMP_PATH)
+ logger.debug("解压 webui_assets 成功...", COMMAND_NAME)
+ else:
+ logger.error("解压 webui_assets 失败...", COMMAND_NAME)
+ return
+ download_file_path = (
+ TMP_PATH / [x for x in os.listdir(TMP_PATH) if (TMP_PATH / x).is_dir()][0]
+ )
+ shutil.rmtree(PUBLIC_PATH, ignore_errors=True)
+ shutil.copytree(download_file_path / "dist", PUBLIC_PATH, dirs_exist_ok=True)
+ logger.debug("复制 webui_assets 成功...", COMMAND_NAME)
+ shutil.rmtree(TMP_PATH, ignore_errors=True)