Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions astrbot/core/config/astrbot_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,14 @@ def __init__(
)
# 检查配置完整性,并插入
has_new = self.check_config_integrity(default_config, conf)

dashboard_conf = conf.get("dashboard")
if isinstance(dashboard_conf, dict):
host_val = dashboard_conf.get("host")
if isinstance(host_val, str) and host_val:
dashboard_conf["host"] = [host_val]
has_new = True
Comment thread
2278535805 marked this conversation as resolved.

reset_dashboard_password = self._consume_reset_dashboard_password_flag()
if reset_dashboard_password and "dashboard" in conf:
self._reset_generated_dashboard_password(conf)
Expand Down
2 changes: 1 addition & 1 deletion astrbot/core/config/default.py
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,7 @@
"password_storage_upgraded": False,
"password_change_required": False,
"jwt_secret": "",
"host": "0.0.0.0",
"host": ["0.0.0.0", "::"],
"port": 6185,
"disable_access_log": True,
"trust_proxy_headers": False,
Expand Down
22 changes: 22 additions & 0 deletions astrbot/core/utils/io.py
Original file line number Diff line number Diff line change
Expand Up @@ -367,10 +367,32 @@ def get_local_ip_addresses():
for addr in addrs:
if addr.family == socket.AF_INET: # 使用 socket.AF_INET 代替 psutil.AF_INET
network_ips.append(addr.address)
elif addr.family == socket.AF_INET6:
address = addr.address
scope_idx = address.find("%")
if scope_idx != -1:
address = address[:scope_idx]
network_ips.append(address)

return network_ips


def normalize_host_list(host_raw: str | list[str] | None) -> list[str]:
"""Normalize a host config value into a list of host strings.

Args:
host_raw: A string, list of strings, or None.

Returns:
A list of non-empty host strings.
"""
if host_raw is None:
return []
if isinstance(host_raw, list):
return [h for h in host_raw if h]
return [h.strip() for h in str(host_raw).split(",") if h.strip()]


def get_dashboard_dist_version(dist_dir: str | Path) -> str | None:
"""Read the WebUI version from a dashboard dist directory.

Expand Down
48 changes: 37 additions & 11 deletions astrbot/dashboard/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
get_dashboard_dist_version,
get_local_ip_addresses,
is_dashboard_dist_compatible,
normalize_host_list,
should_use_bundled_dashboard_dist,
)
from astrbot.dashboard.asgi_runtime import (
Expand Down Expand Up @@ -553,11 +554,12 @@ def run(self):
or os.environ.get("ASTRBOT_DASHBOARD_PORT")
or dashboard_config.get("port", 6185)
)
host = (
host_raw = (
os.environ.get("DASHBOARD_HOST")
or os.environ.get("ASTRBOT_DASHBOARD_HOST")
or dashboard_config.get("host", "0.0.0.0")
or dashboard_config.get("host", ["0.0.0.0"])
)
hosts = normalize_host_list(host_raw)
enable = dashboard_config.get("enable", True)
ssl_config = dashboard_config.get("ssl", {})
if not isinstance(ssl_config, dict):
Expand All @@ -578,17 +580,36 @@ def run(self):
logger.info("WebUI disabled.")
return None

logger.info("Starting WebUI at %s://%s:%s", scheme, host, port)
if host == "0.0.0.0":
bound_urls = [
f"{scheme}://[{h}]:{port}" if ":" in h else f"{scheme}://{h}:{port}"
for h in hosts
]
logger.info("Starting WebUI at %s", ", ".join(bound_urls))
all_interfaces = {"0.0.0.0", "::"}
local_hosts = {"localhost", "127.0.0.1", "::1"}
if all_interfaces & set(hosts):
logger.info(
"WebUI listens on all interfaces. Check security. Set dashboard.host in data/cmd_config.json to change it.",
)

if host not in ["localhost", "127.0.0.1"]:
try:
ip_addr = get_local_ip_addresses()
except Exception as _:
pass
if not set(hosts).issubset(local_hosts):
if all_interfaces & set(hosts):
try:
ip_addr = get_local_ip_addresses()
except Exception as _:
ip_addr = []
has_v4_wildcard = "0.0.0.0" in hosts
has_v6_wildcard = "::" in hosts
if has_v4_wildcard and not has_v6_wildcard:
specific_v6 = [h for h in hosts if ":" in h and h != "::"]
ip_addr = [ip for ip in ip_addr if ":" not in ip] + specific_v6
elif has_v6_wildcard and not has_v4_wildcard:
specific_v4 = [h for h in hosts if ":" not in h and h != "0.0.0.0"]
ip_addr = [ip for ip in ip_addr if ":" in ip] + specific_v4
else:
ip_addr = [h for h in hosts if h not in local_hosts]
else:
ip_addr = []
if isinstance(port, str):
port = int(port)

Expand All @@ -614,7 +635,10 @@ def run(self):
parts = [f"\n ✨✨✨\n AstrBot v{VERSION} {webui_status}\n\n"]
parts.append(f" ➜ Local: {scheme}://localhost:{port}\n")
for ip in ip_addr:
parts.append(f" ➜ Network: {scheme}://{ip}:{port}\n")
if ":" in ip:
parts.append(f" ➜ Network: {scheme}://[{ip}]:{port}\n")
else:
parts.append(f" ➜ Network: {scheme}://{ip}:{port}\n")
parts.append(self._build_dashboard_credentials_display())
display = "".join(parts)

Expand All @@ -627,7 +651,9 @@ def run(self):

# 配置 Hypercorn
config = HyperConfig()
config.bind = [f"{host}:{port}"]
config.bind = [
f"[{h}]:{port}" if ":" in h else f"{h}:{port}" for h in hosts
]
if bool(self.config.get("dashboard", {}).get("trust_proxy_headers", False)):
config.logger_class = _ProxyAwareHypercornLogger
if ssl_enable:
Expand Down
10 changes: 8 additions & 2 deletions astrbot/dashboard/services/auth_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from astrbot.core import DEMO_MODE
from astrbot.core.config.astrbot_config import AstrBotConfig
from astrbot.core.db import BaseDatabase
from astrbot.core.utils.io import normalize_host_list
from astrbot.core.utils.auth_password import (
is_default_dashboard_password,
is_md5_dashboard_password,
Expand Down Expand Up @@ -427,12 +428,17 @@ async def is_setup_required(self) -> bool:
def can_skip_default_password_auth(self) -> bool:
if not self.env_flag_enabled(SKIP_DEFAULT_PASSWORD_AUTH_ENV):
return False
host = (
host_raw = (
os.environ.get("DASHBOARD_HOST")
or os.environ.get("ASTRBOT_DASHBOARD_HOST")
or self.config["dashboard"].get("host", "")
)
return str(host).strip().lower() in LOCAL_DASHBOARD_HOSTS
hosts = normalize_host_list(host_raw)
if not hosts:
return False
return all(
str(h).strip().lower() in LOCAL_DASHBOARD_HOSTS for h in hosts
)

@staticmethod
def env_flag_enabled(name: str) -> bool:
Expand Down