Files
wehub-resource-sync e4dcfc49aa
Tests / Import Check (Python 3.13) (push) Has been cancelled
Tests / Import Check (Python 3.14) (push) Has been cancelled
Tests / Python Tests (Python 3.11) (push) Has been cancelled
Tests / Python Tests (Python 3.12) (push) Has been cancelled
Tests / Python Tests (Python 3.14) (push) Has been cancelled
Tests / Test Summary (push) Has been cancelled
Tests / Lint and Format (push) Has been cancelled
Tests / Web Node Tests (push) Has been cancelled
Tests / Import Check (Python 3.11) (push) Has been cancelled
Tests / Import Check (Python 3.12) (push) Has been cancelled
Tests / Python Tests (Python 3.13) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:00:43 +08:00

124 lines
4.0 KiB
Python

from __future__ import annotations
from dataclasses import dataclass
import json
import os
from pathlib import Path
from typing import Any
from deeptutor.runtime.home import get_runtime_home
from .runtime_settings import RuntimeSettingsService
PROJECT_ROOT = get_runtime_home()
DEFAULT_BACKEND_PORT = 8001
DEFAULT_FRONTEND_PORT = 3782
DEFAULT_LANGUAGE = "en"
@dataclass(frozen=True, slots=True)
class LaunchSettings:
backend_port: int
frontend_port: int
language: str
source: str
settings_dir: Path
interface_json_path: Path
system_json_path: Path
def _load_json_object(path: Path) -> dict[str, Any]:
if not path.exists() or path.stat().st_size == 0:
return {}
try:
loaded = json.loads(path.read_text(encoding="utf-8"))
except Exception:
return {}
return loaded if isinstance(loaded, dict) else {}
def _coerce_port(value: Any) -> int | None:
try:
port = int(value)
except (TypeError, ValueError):
return None
if 1 <= port <= 65535:
return port
return None
def _normalize_language(value: Any) -> str | None:
if not isinstance(value, str):
return None
language = value.strip().lower()
if language in {"en", "english"} or language.startswith("en_"):
return "en"
if language in {"zh", "cn", "chinese"} or language.startswith("zh_"):
return "zh"
return None
def load_launch_settings(project_root: Path | None = None) -> LaunchSettings:
"""Load ports and UI language for launcher-style entry points.
Launch ports come from ``data/user/settings/system.json``. Environment
variables are treated as explicit deployment overrides (for example Docker
port mapping), not as a second application configuration file. UI language
remains in ``data/user/settings/interface.json``.
"""
root = project_root or PROJECT_ROOT
settings_dir = root / "data" / "user" / "settings"
interface_json_path = settings_dir / "interface.json"
system_json_path = settings_dir / "system.json"
interface_settings = _load_json_object(interface_json_path)
service = RuntimeSettingsService.get_instance(settings_dir)
system_from_file = service.load_system(include_process_overrides=False)
system_settings = service.load_system(include_process_overrides=True)
settings_backend_port = _coerce_port(system_from_file.get("backend_port"))
settings_frontend_port = _coerce_port(system_from_file.get("frontend_port"))
process_backend_port = _coerce_port(os.getenv("BACKEND_PORT"))
process_frontend_port = _coerce_port(os.getenv("FRONTEND_PORT"))
interface_language = _normalize_language(interface_settings.get("language"))
process_language = _normalize_language(os.getenv("UI_LANGUAGE")) or _normalize_language(
os.getenv("LANGUAGE")
)
backend_port = _coerce_port(system_settings.get("backend_port")) or DEFAULT_BACKEND_PORT
frontend_port = _coerce_port(system_settings.get("frontend_port")) or DEFAULT_FRONTEND_PORT
language = interface_language or process_language or DEFAULT_LANGUAGE
sources: list[str] = []
if settings_backend_port is not None or settings_frontend_port is not None:
sources.append("data/user/settings/system.json")
if process_backend_port is not None or process_frontend_port is not None:
sources.append("environment")
if not sources:
sources.append("defaults")
if process_language is not None:
sources.append("environment")
if interface_language is not None:
sources.append("data/user/settings/interface.json")
else:
sources.append("default language")
return LaunchSettings(
backend_port=backend_port,
frontend_port=frontend_port,
language=language,
source=" + ".join(sources),
settings_dir=settings_dir,
interface_json_path=interface_json_path,
system_json_path=system_json_path,
)
__all__ = [
"DEFAULT_BACKEND_PORT",
"DEFAULT_FRONTEND_PORT",
"DEFAULT_LANGUAGE",
"LaunchSettings",
"load_launch_settings",
]