Files

131 lines
4.7 KiB
Python

"""Browser-reachable addresses written into generated HTML pages.
The settings dialog is the source of truth. A value saved in
``system_settings.json`` wins over ``config.yaml`` and the built-in default,
so a deployment can point new pages at a domain users can actually open
without editing or restarting from a config file.
"""
from __future__ import annotations
import logging
from urllib.parse import urlparse
from deerflow.config.html_runtime_config import (
DEFAULT_HTML_MAP_TILE_URL,
DEFAULT_HTML_RUNTIME_BASE_URL,
)
logger = logging.getLogger(__name__)
_HTML_RUNTIME_SUFFIX = "/api/public/html-runtime"
def normalize_html_runtime_base_url(value: str) -> str:
"""Return an absolute runtime base, appending the public asset path when omitted."""
raw = str(value or "").strip()
if not raw:
raise ValueError("请填写用户浏览器能够访问的 HTML 运行库地址")
if any(character.isspace() for character in raw):
raise ValueError("HTML 运行库地址不能包含空格")
parsed = urlparse(raw)
if parsed.scheme not in {"http", "https"} or not parsed.netloc:
raise ValueError("HTML 运行库地址需要以 http:// 或 https:// 开头,并包含域名或 IP")
if parsed.username or parsed.password:
raise ValueError("HTML 运行库地址不能包含账号或密码")
if parsed.query or parsed.fragment:
raise ValueError("HTML 运行库地址不需要查询参数")
path = parsed.path.rstrip("/")
if path in {"", "/"}:
path = _HTML_RUNTIME_SUFFIX
return f"{parsed.scheme}://{parsed.netloc}{path}"
def normalize_html_map_tile_url(value: str) -> str:
"""Return a Leaflet tile template containing the x/y/z placeholders."""
raw = str(value or "").strip()
if not raw:
raise ValueError("请填写地图瓦片地址")
if any(character.isspace() for character in raw):
raise ValueError("地图瓦片地址不能包含空格")
parsed = urlparse(raw)
if parsed.scheme not in {"http", "https"} or not parsed.netloc:
raise ValueError("地图瓦片地址需要以 http:// 或 https:// 开头,并包含域名或 IP")
if parsed.username or parsed.password:
raise ValueError("地图瓦片地址不能包含账号或密码")
lowered = raw.lower()
missing = [token for token in ("{z}", "{x}", "{y}") if token not in lowered]
if missing:
raise ValueError("地图瓦片地址需要包含 {z}、{x}、{y} 三个占位符")
return raw
def html_runtime_base_url(fallback: str | None = None) -> str:
"""Return the runtime base used when writing or serving generated HTML."""
saved = _saved("base_url")
if saved:
try:
return normalize_html_runtime_base_url(saved)
except ValueError:
logger.warning("Ignoring invalid saved HTML runtime base URL")
return _first_valid_base(fallback, _config_value("base_url"), DEFAULT_HTML_RUNTIME_BASE_URL)
def html_map_tile_url(fallback: str | None = None) -> str:
"""Return the Leaflet tile template injected into deerflow-runtime.js."""
saved = _saved("map_tile_url")
if saved:
try:
return normalize_html_map_tile_url(saved)
except ValueError:
logger.warning("Ignoring invalid saved HTML map tile URL")
return _first_valid_tile(fallback, _config_value("map_tile_url"), DEFAULT_HTML_MAP_TILE_URL)
def _saved(field: str) -> str:
try:
from deerflow.config.system_settings import load_system_settings
block = load_system_settings().html_runtime
return str(getattr(block, field, "") or "").strip()
except Exception:
logger.debug("HTML runtime settings are unavailable", exc_info=True)
return ""
def _config_value(field: str) -> str:
try:
from deerflow.config import get_app_config
runtime = get_app_config().html_runtime
return str(getattr(runtime, field, "") or "").strip()
except Exception:
logger.debug("HTML runtime config fallback is unavailable", exc_info=True)
return ""
def _first_valid_base(*candidates: str | None) -> str:
for candidate in candidates:
if not candidate or not str(candidate).strip():
continue
try:
return normalize_html_runtime_base_url(str(candidate))
except ValueError:
continue
return DEFAULT_HTML_RUNTIME_BASE_URL
def _first_valid_tile(*candidates: str | None) -> str:
for candidate in candidates:
if not candidate or not str(candidate).strip():
continue
try:
return normalize_html_map_tile_url(str(candidate))
except ValueError:
continue
return DEFAULT_HTML_MAP_TILE_URL