Files
Proxmox-AIS-Server/provisioner/config.py
T
BartelLuis 06c3474636
CI / javascript-check (push) Successful in 51s
CI / container-policy (push) Successful in 2s
CI / container-verify (push) Canceled after 0s
CI / container-publish (push) Canceled after 0s
CI / python-tests (push) Canceled after 6m59s
feat: add Proxmox provisioning service with CI and deployment tooling
2026-09-14 20:09:12 +02:00

65 lines
2.8 KiB
Python

from dataclasses import dataclass, field, fields
import os
from pathlib import Path
import tomllib
from urllib.parse import urlsplit
@dataclass
class Settings:
data_dir: Path = Path("data")
master_key_file: Path = Path("secrets/master.key")
public_url: str = "https://localhost:8080"
secure_cookies: bool = True
bootstrap_username: str | None = None
bootstrap_password: str | None = None
testing: bool = False
session_hours: int = 8
answer_window_seconds: int = 300
enrollment_hours: int = 4
lease_seconds: int = 900
heartbeat_unknown_seconds: int = 180
log_retention_days: int = 30
audit_retention_days: int = 180
max_request_bytes: int = 1048576
four_eyes: bool = True
maintenance: bool = False
trusted_proxy_ips: str = "127.0.0.1"
runner_ca_file: str | None = None
defaults: dict = field(default_factory=dict)
sites: dict = field(default_factory=dict)
def __post_init__(self):
self.data_dir = Path(self.data_dir)
self.master_key_file = Path(self.master_key_file)
self.public_url = self.public_url.rstrip("/")
url = urlsplit(self.public_url)
if not url.hostname or url.username or url.password or url.query or url.fragment or url.path:
raise ValueError("PUBLIC_URL muss eine absolute Basis-URL ohne Pfad sein.")
if url.scheme != "https" and not (url.scheme == "http" and url.hostname in {"localhost", "127.0.0.1", "testserver"}):
raise ValueError("PUBLIC_URL benötigt HTTPS; HTTP ist nur für lokale Entwicklung erlaubt.")
if self.master_key_file.resolve().is_relative_to(self.data_dir.resolve()):
raise ValueError("Der Master-Key muss außerhalb des Datenverzeichnisses liegen.")
for name in ("session_hours", "answer_window_seconds", "enrollment_hours", "lease_seconds", "max_request_bytes"):
if getattr(self, name) <= 0:
raise ValueError(f"{name} muss positiv sein.")
@classmethod
def from_env(cls):
values = {}
if os.environ.get("APP_CONFIG"):
with open(os.environ["APP_CONFIG"], "rb") as stream:
loaded = tomllib.load(stream)
values.update(loaded.get("app", loaded))
bools = {"secure_cookies", "testing", "four_eyes", "maintenance"}
ints = {f.name for f in fields(cls) if f.type is int}
allowed = {f.name for f in fields(cls)}
unknown = set(values) - allowed
if unknown:
raise ValueError(f"Unbekannte Konfiguration: {', '.join(sorted(unknown))}")
for key in allowed:
value = os.environ.get(key.upper())
if value is not None:
values[key] = value.lower() in {"true", "1", "yes"} if key in bools else int(value) if key in ints else value
return cls(**values)