From dcf84e6726bc55e7e25ba1773c9336fec614ffc4 Mon Sep 17 00:00:00 2001 From: LIghtJUNction Date: Fri, 10 Apr 2026 21:58:52 +0800 Subject: [PATCH] feat: enhance CLI prompts and fix frontend 401 handling - cmd_conf: show password rules before setting, display length after input - cmd_init/cmd_run: add Chinese prompts and HTTPS security warnings - openai_source/gemini_source: add missing provider adapter import - ConsoleDisplayer: add missing resolveApiUrl import for SSE - WelcomePage: show user-friendly message for 401 unauthorized errors --- astrbot/cli/commands/cmd_conf.py | 10 +++ astrbot/cli/commands/cmd_init.py | 63 ++++++++++++++----- astrbot/cli/commands/cmd_run.py | 30 ++++++++- .../core/provider/sources/gemini_source.py | 1 + .../core/provider/sources/openai_source.py | 1 + .../components/shared/ConsoleDisplayer.vue | 4 +- dashboard/src/views/WelcomePage.vue | 8 ++- 7 files changed, 94 insertions(+), 23 deletions(-) diff --git a/astrbot/cli/commands/cmd_conf.py b/astrbot/cli/commands/cmd_conf.py index c072b38d9..7d99a6785 100644 --- a/astrbot/cli/commands/cmd_conf.py +++ b/astrbot/cli/commands/cmd_conf.py @@ -164,7 +164,17 @@ def _get_nested_item(obj: dict[str, Any], path: str) -> Any: def prompt_dashboard_password(prompt: str = "Dashboard password") -> str: + # 显示密码规则提示 + click.echo() + click.echo("密码规则:") + click.echo(" - 至少 12 个字符") + click.echo(" - 必须包含至少一个大写字母") + click.echo(" - 必须包含至少一个小写字母") + click.echo(" - 必须包含至少一个数字") + click.echo() + password = click.prompt(prompt, hide_input=True, confirmation_prompt=True, type=str) + click.echo(f"密码长度: {len(password)} 字符") return _validate_dashboard_password(password) diff --git a/astrbot/cli/commands/cmd_init.py b/astrbot/cli/commands/cmd_init.py index 7ba98535f..6c8577793 100644 --- a/astrbot/cli/commands/cmd_init.py +++ b/astrbot/cli/commands/cmd_init.py @@ -23,15 +23,20 @@ async def initialize_astrbot( admin_password: str | None, ) -> None: """Execute AstrBot initialization logic""" + click.echo("=" * 60) + click.echo("AstrBot 初始化向导") + click.echo("=" * 60) + click.echo() + dot_astrbot = astrbot_root / ".astrbot" if not dot_astrbot.exists(): if yes or click.confirm( - f"Install AstrBot to this directory? {astrbot_root}", + f"确定要将 AstrBot 安装到以下目录吗?\n {astrbot_root}", default=True, abort=True, ): dot_astrbot.touch() - click.echo(f"Created {dot_astrbot}") + click.echo(f"[OK] 已创建: {dot_astrbot}") paths = { "data": astrbot_root / "data", "config": astrbot_root / "data" / "config", @@ -41,16 +46,15 @@ async def initialize_astrbot( } for name, path in paths.items(): path.mkdir(parents=True, exist_ok=True) - click.echo( - f"{('Created' if not path.exists() else f'{name} Directory exists')}: {path}", - ) + status = "Created" if not path.exists() else "Exists" + click.echo(f" [{status}] {name.title()}: {path}") config_path = astrbot_root / "data" / "cmd_config.json" if not config_path.exists(): config_path.write_text( json.dumps(DEFAULT_CONFIG, ensure_ascii=False, indent=2), encoding="utf-8-sig", ) - click.echo(f"Created config file: {config_path}") + click.echo(f"[OK] 配置文件已创建: {config_path}") ASTRBOT_ROOT = astrbot_root env_file = ASTRBOT_ROOT / ".env" if not env_file.exists(): @@ -80,11 +84,11 @@ async def initialize_astrbot( header = f"# Generated from config.template by astrbot init for instance: {instance_name}\n# This file will be auto-loaded by 'astrbot run'\n\n" env_file.write_text(header + txt, encoding="utf-8") env_file.chmod(420) - click.echo(f"Created environment file from template: {env_file}") + click.echo(f"[OK] 环境变量文件已创建: {env_file}") except Exception as e: - click.echo(f"Warning: failed to generate .env from template: {e!s}") + click.echo(f"[警告] 无法从模板生成 .env 文件: {e!s}") else: - click.echo("No config.template found; skipping .env generation") + click.echo("[提示] 未找到 config.template 文件,跳过 .env 生成") if admin_password is not None: raise click.ClickException( "--admin-password is no longer supported during init. Run 'astrbot conf admin' after initialization.", @@ -105,20 +109,36 @@ async def initialize_astrbot( json.dumps(config, ensure_ascii=False, indent=2), encoding="utf-8-sig", ) - click.echo(f"Configured dashboard admin username: {effective_admin_username}") - click.echo( - "Dashboard password is not initialized for interactive use. Run 'astrbot conf admin' before the first login.", - ) + click.echo(f"[OK] Dashboard admin 用户名已设置为: {effective_admin_username}") + click.echo() + click.echo("!" * 60) + click.echo("重要提示:") + click.echo(" 1. Dashboard 密码尚未设置!首次登录前必须先设置密码") + click.echo(" 2. 设置命令: astrbot conf admin") + click.echo(" 3. 登录地址: http://localhost:6185 或 http://服务器IP:6185") + click.echo("!" * 60) + click.echo() if not backend_only and ( yes or click.confirm( - "是否需要集成式 WebUI?(个人电脑推荐,服务器不推荐)", + "是否需要集成式 WebUI?(个人电脑推荐,服务器推荐使用后端模式)", default=True, ) ): await DashboardManager().ensure_installed(astrbot_root) else: - click.echo("你可以使用在线面版(需支持配置后端)来控制。") + click.echo() + click.echo("[提示] 你选择了后端模式,可以使用以下方式管理 AstrBot:") + click.echo(" - 使用在线 Dashboard: 在浏览器中访问远程服务器的 WebUI") + click.echo(" - 使用 CLI 命令: astrbot conf / astrbot plug 等") + click.echo() + click.echo("!" * 60) + click.echo("安全提示:") + click.echo(" HTTPS 前端只能安全连接 localhost 的 HTTP 后端") + click.echo(" 不支持远程 + HTTP 后端(不安全)") + click.echo(" 如需远程访问,请使用 HTTPS 后端或通过反向代理") + click.echo("!" * 60) + click.echo() @click.command() @@ -179,7 +199,18 @@ def init( backup_file=backup, yes=True, ) - click.echo("Done! You can now run 'astrbot run' to start AstrBot") + click.echo() + click.echo("=" * 60) + click.echo("初始化完成!") + click.echo("=" * 60) + click.echo() + click.echo("启动 AstrBot:") + click.echo(" 完整模式(含 Dashboard): astrbot run") + click.echo(" 仅后端模式: astrbot run --backend-only") + click.echo() + click.echo("首次使用前请先设置管理员密码:") + click.echo(" astrbot conf admin") + click.echo() except Timeout: raise click.ClickException( "Cannot acquire lock file. Please check if another instance is running", diff --git a/astrbot/cli/commands/cmd_run.py b/astrbot/cli/commands/cmd_run.py index a716ec519..a2bf0d81f 100644 --- a/astrbot/cli/commands/cmd_run.py +++ b/astrbot/cli/commands/cmd_run.py @@ -378,10 +378,34 @@ def run( except asyncio.CancelledError: pass - click.echo("AstrBot is running... (streaming logs)") + click.echo() + click.echo("=" * 60) + click.echo("AstrBot 启动中...") + click.echo("=" * 60) + click.echo() + if backend_only: - click.echo("Dashboard: https://dash.astrbot.men/") - click.echo("Backend: localhost or based on https") + click.echo("[模式] 仅后端模式(无本地 Dashboard)") + click.echo() + click.echo("[提示] 可以通过以下方式访问 WebUI:") + click.echo(" - 使用远程服务器的在线 Dashboard") + click.echo(" - 地址: http://服务器IP:6185") + click.echo() + else: + dashboard_url = f"http://{host or 'localhost'}:{port or '6185'}" + click.echo(f"[模式] 完整模式(含本地 Dashboard)") + click.echo() + click.echo(f"[Dashboard] 请访问: {dashboard_url}") + click.echo() + click.echo("!" * 60) + click.echo("安全提示:") + click.echo(" HTTPS 前端只能安全连接 localhost 的 HTTP 后端") + click.echo(" 不支持远程 + HTTP 后端(不安全)") + click.echo("!" * 60) + click.echo() + + click.echo("正在启动服务...(日志输出中)") + click.echo() asyncio.run(run_with_logging()) except KeyboardInterrupt: diff --git a/astrbot/core/provider/sources/gemini_source.py b/astrbot/core/provider/sources/gemini_source.py index ca76f645f..e6c66bb09 100644 --- a/astrbot/core/provider/sources/gemini_source.py +++ b/astrbot/core/provider/sources/gemini_source.py @@ -26,6 +26,7 @@ from astrbot.core.utils.astrbot_path import get_astrbot_temp_path from astrbot.core.utils.io import download_file, download_image_by_url from astrbot.core.utils.media_utils import ensure_wav from astrbot.core.utils.network_utils import is_connection_error, log_connection_failure +from astrbot.core.provider.register import register_provider_adapter class SuppressNonTextPartsWarning(logging.Filter): diff --git a/astrbot/core/provider/sources/openai_source.py b/astrbot/core/provider/sources/openai_source.py index 24e4efdda..783f0b707 100644 --- a/astrbot/core/provider/sources/openai_source.py +++ b/astrbot/core/provider/sources/openai_source.py @@ -45,6 +45,7 @@ from astrbot.core.utils.network_utils import ( log_connection_failure, ) from astrbot.core.utils.string_utils import normalize_and_dedupe_strings +from astrbot.core.provider.register import register_provider_adapter @register_provider_adapter( diff --git a/dashboard/src/components/shared/ConsoleDisplayer.vue b/dashboard/src/components/shared/ConsoleDisplayer.vue index 65a8c0b36..839182e64 100644 --- a/dashboard/src/components/shared/ConsoleDisplayer.vue +++ b/dashboard/src/components/shared/ConsoleDisplayer.vue @@ -1,6 +1,6 @@ @@ -135,7 +135,7 @@ export default { const token = localStorage.getItem("token"); - this.eventSource = new EventSourcePolyfill("/api/live-log", { + this.eventSource = new EventSourcePolyfill(resolveApiUrl("/api/live-log"), { headers: { Authorization: token ? `Bearer ${token}` : "", }, diff --git a/dashboard/src/views/WelcomePage.vue b/dashboard/src/views/WelcomePage.vue index d440acf73..91a93da34 100644 --- a/dashboard/src/views/WelcomePage.vue +++ b/dashboard/src/views/WelcomePage.vue @@ -446,10 +446,14 @@ async function checkAndSaveBackend() { if ((platformConfigData.value.platform || []).length > 0) { platformStepState.value = "completed"; } - } catch (error) { + } catch (error: any) { setApiBaseUrl(originalBase); backendStepState.value = "pending"; - showError(`Failed to connect to backend: ${error}`); + if (error?.response?.status === 401) { + showError("无访问权限!请点击右上角退出登录!"); + } else { + showError(`Failed to connect to backend: ${error}`); + } } finally { checkingBackend.value = false; }