Files
homelable/backend/app/core/config.py
T
Pouzor 210304394e feat: read-only live view at /view?key=<LIVEVIEW_KEY>
Implements issue #5. Off by default; set LIVEVIEW_KEY in .env to enable.
No JWT required — key-based auth via ?key= query param.
Returns 403 when disabled or key is wrong.
Read-only ReactFlow canvas (pan/zoom, no editing).
Standalone mode loads from localStorage without a key.
Includes 8 backend tests and 9 frontend tests.
2026-03-28 15:27:54 +01:00

62 lines
2.2 KiB
Python

import json
from pathlib import Path
from pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings):
model_config = SettingsConfigDict(env_file=".env", env_file_encoding="utf-8")
secret_key: str # Required — set SECRET_KEY in .env
sqlite_path: str = "./data/homelab.db"
cors_origins: list[str] = ["http://localhost:5173", "http://localhost:3000"]
# JWT
algorithm: str = "HS256"
access_token_expire_minutes: int = 1440 # 24h
# Auth — set AUTH_USERNAME and AUTH_PASSWORD_HASH in .env
auth_username: str = "admin"
auth_password_hash: str = ""
# Scanner
scanner_ranges: list[str] = ["192.168.1.0/24"]
# Status checker
status_checker_interval: int = 60
# MCP service key — set MCP_SERVICE_KEY in .env
# Used by the MCP server to authenticate against the backend without a user password.
# Leave empty to disable MCP service key auth.
mcp_service_key: str = ""
# Live view — optional read-only public canvas endpoint.
# Set to a random secret string to enable /api/v1/liveview?key=<value>.
# Leave unset (or empty) to keep the feature disabled (default).
liveview_key: str | None = None
def _override_path(self) -> Path:
return Path(self.sqlite_path).parent / "scan_config.json"
def load_overrides(self) -> None:
"""Load runtime scan config overrides written by the API."""
try:
data = json.loads(self._override_path().read_text())
if "scanner_ranges" in data:
self.scanner_ranges = data["scanner_ranges"]
if "status_checker_interval" in data:
self.status_checker_interval = int(data["status_checker_interval"])
except Exception:
pass
def save_overrides(self) -> None:
"""Persist scan config so it survives container restarts."""
self._override_path().parent.mkdir(parents=True, exist_ok=True)
self._override_path().write_text(json.dumps({
"scanner_ranges": self.scanner_ranges,
"status_checker_interval": self.status_checker_interval,
}))
settings = Settings() # type: ignore[call-arg]