From e0617969971fca00c914f65da708176598841b8fec5e1c944d1df6670788660b Mon Sep 17 00:00:00 2001 From: wlfb Date: Mon, 20 Jul 2026 22:56:26 +0200 Subject: [PATCH] feat: FB_GRAFANA_PUBLIC_URL + FB_SESSION_COOKIE_SECURE in Settings --- finance/app/config.py | 16 ++++++++++++++ finance/tests/test_config.py | 42 ++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/finance/app/config.py b/finance/app/config.py index 7b36a76..89a19bb 100644 --- a/finance/app/config.py +++ b/finance/app/config.py @@ -5,6 +5,12 @@ from functools import lru_cache from pathlib import Path +def _env_bool(value: str) -> bool: + """Interpretiert einen Env-Wert als Wahrheitswert. Truthy sind (case- + insensitiv) 1/true/yes/on; alles andere (inkl. leer) ist False.""" + return value.strip().lower() in ("1", "true", "yes", "on") + + @dataclass(frozen=True) class Settings: database_url: str @@ -18,6 +24,8 @@ class Settings: horizon_days: int env_file: Path grafana_url: str + grafana_public_url: str + session_cookie_secure: bool @lru_cache @@ -38,4 +46,12 @@ def get_settings() -> Settings: # 4 Task 2). Default passt zum Container-Mountpunkt "/data/.env". env_file=Path(e("FB_ENV_FILE", "/data/.env")), grafana_url=e("FB_GRAFANA_URL", "http://localhost:3000"), + # Öffentliche Grafana-Basis-URL für Browser-Links/iframes hinter einem + # Reverse Proxy (z.B. 'https://fb.wolfundlaemmlein.de/grafana/'). Leer = + # Direktbetrieb, Templates fallen auf http://:8097 zurück. NICHT + # zu verwechseln mit grafana_url (intern, Server→Grafana). + grafana_public_url=e("FB_GRAFANA_PUBLIC_URL", ""), + # Session-Cookie mit secure-Flag ausliefern (nur über HTTPS gültig). + # Für den Reverse-Proxy-Betrieb; Default False für lokalen HTTP-Zugriff. + session_cookie_secure=_env_bool(e("FB_SESSION_COOKIE_SECURE", "")), ) diff --git a/finance/tests/test_config.py b/finance/tests/test_config.py index 9e2de0a..5c470a5 100644 --- a/finance/tests/test_config.py +++ b/finance/tests/test_config.py @@ -1,5 +1,7 @@ from pathlib import Path +import pytest + from app.config import get_settings @@ -35,3 +37,43 @@ def test_grafana_url_default(monkeypatch): assert get_settings().grafana_url == "http://localhost:3000" finally: get_settings.cache_clear() + + +def test_grafana_public_url_default(monkeypatch): + monkeypatch.delenv("FB_GRAFANA_PUBLIC_URL", raising=False) + get_settings.cache_clear() + try: + assert get_settings().grafana_public_url == "" + finally: + get_settings.cache_clear() + + +def test_grafana_public_url_from_env(monkeypatch): + monkeypatch.setenv("FB_GRAFANA_PUBLIC_URL", "https://fb.example.de/grafana/") + get_settings.cache_clear() + try: + assert get_settings().grafana_public_url == "https://fb.example.de/grafana/" + finally: + get_settings.cache_clear() + + +def test_session_cookie_secure_default_false(monkeypatch): + monkeypatch.delenv("FB_SESSION_COOKIE_SECURE", raising=False) + get_settings.cache_clear() + try: + assert get_settings().session_cookie_secure is False + finally: + get_settings.cache_clear() + + +@pytest.mark.parametrize("val,expected", [ + ("true", True), ("True", True), ("1", True), ("yes", True), ("on", True), + ("false", False), ("0", False), ("", False), ("nope", False), +]) +def test_session_cookie_secure_parsing(monkeypatch, val, expected): + monkeypatch.setenv("FB_SESSION_COOKIE_SECURE", val) + get_settings.cache_clear() + try: + assert get_settings().session_cookie_secure is expected + finally: + get_settings.cache_clear()