From 7e35307f040075d9d54c32b0f50a720ab933d7e8 Mon Sep 17 00:00:00 2001
From: Soulter <37870767+Soulter@users.noreply.github.com>
Date: Mon, 31 Aug 2026 10:35:33 +0800
Subject: [PATCH 1/2] fix: defer dashboard setup until startup (#9879)
* fix: defer dashboard setup until startup
* refactor(migration): remove logging statements from webchat session migration
---
astrbot/cli/commands/cmd_init.py | 13 ----
.../db/migration/migra_webchat_session.py | 17 +----
astrbot/dashboard/api/static_files.py | 6 +-
.../dashboard/services/static_file_service.py | 67 ++++++++++++++++++-
tests/test_cli_init.py | 14 +---
tests/test_fastapi_v1_dashboard.py | 7 ++
6 files changed, 77 insertions(+), 47 deletions(-)
diff --git a/astrbot/cli/commands/cmd_init.py b/astrbot/cli/commands/cmd_init.py
index d418d79d76..f22961b7c9 100644
--- a/astrbot/cli/commands/cmd_init.py
+++ b/astrbot/cli/commands/cmd_init.py
@@ -8,17 +8,6 @@
DASHBOARD_INITIAL_PASSWORD_ENV = "ASTRBOT_DASHBOARD_INITIAL_PASSWORD"
-async def check_dashboard(astrbot_root: Path) -> None:
- """Check whether dashboard assets are available.
-
- Args:
- astrbot_root: AstrBot data directory path.
- """
- from ..utils import check_dashboard as _check_dashboard
-
- await _check_dashboard(astrbot_root)
-
-
def _initialize_config_from_env(astrbot_root: Path) -> None:
if DASHBOARD_INITIAL_PASSWORD_ENV not in os.environ:
return
@@ -60,8 +49,6 @@ async def initialize_astrbot(astrbot_root: Path, yes: bool = False) -> None:
_initialize_config_from_env(astrbot_root)
- await check_dashboard(astrbot_root / "data")
-
@click.command()
@click.option("--yes", "-y", is_flag=True, help="Skip confirmation prompts")
diff --git a/astrbot/core/db/migration/migra_webchat_session.py b/astrbot/core/db/migration/migra_webchat_session.py
index 628743eaed..5775cbe017 100644
--- a/astrbot/core/db/migration/migra_webchat_session.py
+++ b/astrbot/core/db/migration/migra_webchat_session.py
@@ -12,7 +12,6 @@
from sqlalchemy import func, select
from sqlmodel import col
-from astrbot.api import logger
from astrbot.core.db import BaseDatabase
from astrbot.core.db.po import ConversationV2, PlatformMessageHistory, PlatformSession
@@ -23,8 +22,6 @@ async def migrate_webchat_session(db_helper: BaseDatabase) -> None:
This migration extracts all unique user_ids from platform_message_history
where platform_id='webchat' and creates corresponding PlatformSession records.
"""
- logger.info("开始执行数据库迁移(WebChat 会话迁移)...")
-
try:
async with db_helper.get_db() as session:
# 从 platform_message_history 创建 PlatformSession
@@ -44,11 +41,8 @@ async def migrate_webchat_session(db_helper: BaseDatabase) -> None:
webchat_users = result.all()
if not webchat_users:
- logger.info("没有找到需要迁移的 WebChat 数据")
return
- logger.info(f"找到 {len(webchat_users)} 个 WebChat 会话需要迁移")
-
# 检查已存在的会话
existing_query = select(col(PlatformSession.session_id))
existing_result = await session.execute(existing_query)
@@ -83,7 +77,6 @@ async def migrate_webchat_session(db_helper: BaseDatabase) -> None:
# 检查是否已经存在该会话
if session_id in existing_session_ids:
- logger.debug(f"会话 {session_id} 已存在,跳过")
skipped_count += 1
continue
@@ -106,13 +99,5 @@ async def migrate_webchat_session(db_helper: BaseDatabase) -> None:
if sessions_to_add:
session.add_all(sessions_to_add)
await session.commit()
-
- logger.info(
- f"WebChat 会话迁移完成!成功迁移: {len(sessions_to_add)}, 跳过: {skipped_count}",
- )
- else:
- logger.info("没有新会话需要迁移")
-
- except Exception as e:
- logger.error(f"迁移过程中发生错误: {e}", exc_info=True)
+ except Exception:
raise
diff --git a/astrbot/dashboard/api/static_files.py b/astrbot/dashboard/api/static_files.py
index 02aaa3ca69..8f873359d2 100644
--- a/astrbot/dashboard/api/static_files.py
+++ b/astrbot/dashboard/api/static_files.py
@@ -1,7 +1,7 @@
from __future__ import annotations
from fastapi import APIRouter, HTTPException, Request
-from fastapi.responses import FileResponse, PlainTextResponse
+from fastapi.responses import FileResponse, HTMLResponse
from astrbot.dashboard.services.static_file_service import StaticFileService
@@ -13,8 +13,8 @@ def _static_folder(request: Request) -> str | None:
return getattr(request.app.state, "dashboard_static_folder", None)
-def _not_found_response() -> PlainTextResponse:
- return PlainTextResponse(service.get_not_found_message(), status_code=404)
+def _not_found_response() -> HTMLResponse:
+ return HTMLResponse(service.get_not_found_message(), status_code=404)
async def serve_index(request: Request):
diff --git a/astrbot/dashboard/services/static_file_service.py b/astrbot/dashboard/services/static_file_service.py
index b907ff467d..ca152e8663 100644
--- a/astrbot/dashboard/services/static_file_service.py
+++ b/astrbot/dashboard/services/static_file_service.py
@@ -26,9 +26,70 @@ class StaticFileService:
"/tool-use",
)
NOT_FOUND_MESSAGE = (
- "404 Not found。如果你初次使用打开面板发现 404, 请参考文档: "
- "https://docs.astrbot.app/faq.html。如果你正在测试回调地址可达性,"
- "显示这段文字说明测试成功了。"
+ ""
+ ''
+ "
"
+ ''
+ ''
+ "404 · WebUI 文件缺失 / WebUI files are missing"
+ ""
+ ""
+ ""
+ '404 · NOT FOUND
'
+ "WebUI 文件缺失
"
+ "AstrBot 会在启动时检测并尝试下载 WebUI 文件。看到此页面说明下载失败或文件不完整,"
+ "请先尝试重启 AstrBot。
"
+ "手动安装
"
+ "如果问题仍然存在,请前往 Releases 下载与当前 AstrBot 版本匹配的 "
+ "AstrBot-vx.x.x-dashboard.zip,解压后将 "
+ "dist 文件夹放入 AstrBot/data/。
"
+ ''
+ "前往 Releases 下载"
+ ''
+ "WebUI files are missing
"
+ "AstrBot checks for WebUI files and attempts to download them at startup. "
+ "If you see this page, the download failed or the files are incomplete. "
+ "Try restarting AstrBot first.
"
+ "Manual installation
"
+ "If the issue persists, open Releases and download "
+ "AstrBot-vx.x.x-dashboard.zip matching your current AstrBot version. "
+ "Extract it and place the dist folder under "
+ "AstrBot/data/.
"
+ ''
+ "Open Releases"
+ ""
+ '目录结构 / Directory structure
'
+ "AstrBot/\n└── data/\n └── dist/\n ├── index.html\n"
+ " └── assets/
"
+ '正在测试回调地址?看到此页面表示地址可达。
'
+ 'Testing a callback URL? This page confirms that the URL is '
+ "reachable.
"
+ ""
)
def list_index_routes(self) -> tuple[str, ...]:
diff --git a/tests/test_cli_init.py b/tests/test_cli_init.py
index 4713a421a8..5b131c1cc6 100644
--- a/tests/test_cli_init.py
+++ b/tests/test_cli_init.py
@@ -8,14 +8,11 @@
def test_init_yes_skips_install_confirmation(monkeypatch, tmp_path):
- async def fake_check_dashboard(_data_path):
- return None
-
def fail_confirm(*_args, **_kwargs):
pytest.fail("-y should skip the installation confirmation")
monkeypatch.chdir(tmp_path)
- monkeypatch.setattr(cmd_init, "check_dashboard", fake_check_dashboard)
+ monkeypatch.delenv(cmd_init.DASHBOARD_INITIAL_PASSWORD_ENV, raising=False)
monkeypatch.setattr(cmd_init.click, "confirm", fail_confirm)
result = CliRunner().invoke(cmd_init.init, ["-y"])
@@ -25,6 +22,7 @@ def fail_confirm(*_args, **_kwargs):
assert (tmp_path / "data" / "config").is_dir()
assert (tmp_path / "data" / "plugins").is_dir()
assert (tmp_path / "data" / "temp").is_dir()
+ assert not (tmp_path / "data" / "cmd_config.json").exists()
@pytest.mark.asyncio
@@ -32,11 +30,7 @@ async def test_init_without_initial_password_env_does_not_create_config(
monkeypatch,
tmp_path,
):
- async def fake_check_dashboard(_data_path):
- return None
-
monkeypatch.delenv(cmd_init.DASHBOARD_INITIAL_PASSWORD_ENV, raising=False)
- monkeypatch.setattr(cmd_init, "check_dashboard", fake_check_dashboard)
(tmp_path / ".astrbot").touch()
await cmd_init.initialize_astrbot(tmp_path)
@@ -49,12 +43,8 @@ async def test_init_uses_initial_password_env_to_create_config(
monkeypatch,
tmp_path,
):
- async def fake_check_dashboard(_data_path):
- return None
-
initial_password = "AstrBotInitialPassword123"
monkeypatch.setenv(cmd_init.DASHBOARD_INITIAL_PASSWORD_ENV, initial_password)
- monkeypatch.setattr(cmd_init, "check_dashboard", fake_check_dashboard)
(tmp_path / ".astrbot").touch()
await cmd_init.initialize_astrbot(tmp_path)
diff --git a/tests/test_fastapi_v1_dashboard.py b/tests/test_fastapi_v1_dashboard.py
index 8a104aff46..7f16393861 100644
--- a/tests/test_fastapi_v1_dashboard.py
+++ b/tests/test_fastapi_v1_dashboard.py
@@ -1186,6 +1186,13 @@ async def test_dashboard_static_dist_files_are_served(
assert page_response.status_code == 200
assert "/assets/index-demo.js" in page_response.text
assert missing_response.status_code == 404
+ assert missing_response.headers["content-type"].startswith("text/html")
+ assert "请先尝试重启 AstrBot" in missing_response.text
+ assert "手动安装
" in missing_response.text
+ assert "WebUI files are missing" in missing_response.text
+ assert "Manual installation" in missing_response.text
+ assert "AstrBot-vx.x.x-dashboard.zip" in missing_response.text
+ assert "index.html" in missing_response.text
assert traversal_response.status_code == 404
assert api_response.status_code == 404
From 61d2b490537de7a1901b96922fd494f96dea12c8 Mon Sep 17 00:00:00 2001
From: Soulter <905617992@qq.com>
Date: Tue, 1 Sep 2026 01:03:18 +0800
Subject: [PATCH 2/2] chore: bump version to 4.27.5
---
astrbot/__init__.py | 2 +-
changelogs/v4.27.5.md | 17 +++++++++++++++++
pyproject.toml | 2 +-
3 files changed, 19 insertions(+), 2 deletions(-)
create mode 100644 changelogs/v4.27.5.md
diff --git a/astrbot/__init__.py b/astrbot/__init__.py
index 59d03e3127..8c7db6ff8d 100644
--- a/astrbot/__init__.py
+++ b/astrbot/__init__.py
@@ -1,4 +1,4 @@
import logging
-__version__ = "4.27.4"
+__version__ = "4.27.5"
logger = logging.getLogger("astrbot")
diff --git a/changelogs/v4.27.5.md b/changelogs/v4.27.5.md
new file mode 100644
index 0000000000..ff5819df56
--- /dev/null
+++ b/changelogs/v4.27.5.md
@@ -0,0 +1,17 @@
+## [4.27.5] - 2026-09-01
+
+### Fixed
+
+- Fixed an issue where the initial random password was not displayed when running `astrbot run` for the first time. (#9879)
+
+Tips: You can also use `astrbot password` to change the password.
+
+## 中文
+
+### 修复
+
+- 修复了首次执行 `astrbot run` 时未显示初始随机密码的问题。(#9879)
+
+Tips: 也可以使用 `astrbot password` 修改密码。
+
+[4.27.5]: https://github.com/AstrBotDevs/AstrBot/compare/v4.27.4...v4.27.5
diff --git a/pyproject.toml b/pyproject.toml
index 9084d3dcea..1bbaaf043c 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[project]
name = "AstrBot"
-version = "4.27.4"
+version = "4.27.5"
description = "Easy-to-use multi-platform LLM chatbot and development framework"
readme = "README.md"
license = { text = "AGPL-3.0-or-later" }