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
This commit is contained in:
LIghtJUNction
2026-04-10 21:58:52 +08:00
parent 5303e40931
commit dcf84e6726
7 changed files with 94 additions and 23 deletions
+10
View File
@@ -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)
+47 -16
View File
@@ -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",
+27 -3
View File
@@ -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:
@@ -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):
@@ -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(
@@ -1,6 +1,6 @@
<script setup>
import { useCommonStore } from "@/stores/common";
import axios from "@/utils/request";
import axios, { resolveApiUrl } from "@/utils/request";
import { EventSourcePolyfill } from "event-source-polyfill";
</script>
@@ -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}` : "",
},
+6 -2
View File
@@ -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;
}