"""YakPanel - Configuration""" import os from typing import Any from pydantic_settings import BaseSettings from functools import lru_cache # Runtime config loaded from DB on startup (overrides Settings) _runtime_config: dict[str, Any] = {} class Settings(BaseSettings): """Application settings""" app_name: str = "YakPanel" app_version: str = "1.0.0" debug: bool = False # Paths (Ubuntu/Debian default) panel_path: str = "/www/server/YakPanel-server" setup_path: str = "/www/server" www_root: str = "/www/wwwroot" www_logs: str = "/www/wwwlogs" vhost_path: str = "/www/server/panel/vhost" # Database (relative SQLite paths are resolved to backend/data/, not process CWD) database_url: str = "sqlite+aiosqlite:///./data/default.db" # Redis redis_url: str = "redis://localhost:6379/0" # Auth secret_key: str = "YakPanel-server-secret-change-in-production" algorithm: str = "HS256" access_token_expire_minutes: int = 60 * 24 # 24 hours # Panel panel_port: int = 8888 webserver_type: str = "nginx" # nginx, apache, openlitespeed # CORS (comma-separated origins, e.g. https://panel.example.com) cors_extra_origins: str = "" # Remote SSH installer (disabled by default — high risk; see docs) enable_remote_installer: bool = False remote_install_default_url: str = "https://www.yakpanel.com/YakPanel-server/install.sh" remote_install_rate_limit_per_ip: int = 10 remote_install_rate_window_minutes: int = 60 # Comma-separated CIDRs; empty = no restriction (e.g. "10.0.0.0/8,192.168.0.0/16") remote_install_allowed_target_cidrs: str = "" class Config: env_file = ".env" env_file_encoding = "utf-8" @lru_cache def get_settings() -> Settings: return Settings() def get_runtime_config() -> dict[str, Any]: """Get effective panel config (Settings + DB overrides).""" s = get_settings() base = { "panel_port": s.panel_port, "www_root": s.www_root, "setup_path": s.setup_path, "www_logs": s.www_logs, "vhost_path": s.vhost_path, "webserver_type": s.webserver_type, "mysql_root": "", } for k, v in _runtime_config.items(): if k in base: if k == "panel_port": try: base[k] = int(v) except (ValueError, TypeError): pass else: base[k] = v base["backup_path"] = os.path.join(base["setup_path"], "backup") return base def set_runtime_config_overrides(overrides: dict[str, str]) -> None: """Set runtime config from DB (called on startup).""" global _runtime_config _runtime_config = dict(overrides)