From a575c57d5bb7e19c9887a0a69018882ab616de01247450f398f69a0f6f7b178c Mon Sep 17 00:00:00 2001
From: wlfb
Date: Tue, 21 Jul 2026 07:11:27 +0200
Subject: [PATCH] feat: Grafana-Links aus FB_GRAFANA_PUBLIC_URL
(Sub-Pfad-tauglich)
---
finance/app/routers/gui.py | 17 ++++++++++++++++
finance/app/templates/base.html | 2 +-
finance/app/templates/index.html | 4 ++--
finance/app/templates/szenarien.html | 2 +-
finance/tests/test_gui.py | 30 ++++++++++++++++++++++++++++
5 files changed, 51 insertions(+), 4 deletions(-)
diff --git a/finance/app/routers/gui.py b/finance/app/routers/gui.py
index b5b71ed..eb75313 100644
--- a/finance/app/routers/gui.py
+++ b/finance/app/routers/gui.py
@@ -7,6 +7,7 @@ from sqlalchemy import func, select
from sqlalchemy.orm import Session
from app.auth import COOKIE, session_valid
+from app.config import get_settings
from app.db import get_session
from app.engine.loans import add_months
from app.engine.recurrence import occurrences
@@ -30,6 +31,22 @@ PAGE_SIZE = 50
templates = Jinja2Templates(directory="app/templates")
templates.env.globals["app_version"] = get_version()
+
+
+def grafana_public_base(request: Request) -> str:
+ """Öffentliche Grafana-Basis-URL für Browser-Links/iframes. Ist
+ FB_GRAFANA_PUBLIC_URL gesetzt (Reverse-Proxy-Betrieb, z.B.
+ 'https://fb.example.de/grafana/'), wird sie ohne abschließenden
+ Slash zurückgegeben; sonst der lokale Fallback http://:8097
+ (Direktbetrieb ohne Proxy). Templates hängen bei Bedarf '/d/...' an."""
+ public = get_settings().grafana_public_url
+ if public:
+ return public.rstrip("/")
+ host = request.url.hostname or "127.0.0.1"
+ return f"http://{host}:8097"
+
+
+templates.env.globals["grafana_public_base"] = grafana_public_base
templates.env.filters["eur"] = eur
templates.env.filters["de_label"] = de_label
router = APIRouter()
diff --git a/finance/app/templates/base.html b/finance/app/templates/base.html
index c334915..b672731 100644
--- a/finance/app/templates/base.html
+++ b/finance/app/templates/base.html
@@ -17,7 +17,7 @@
Szenarien
Admin
Hilfe
- Grafana
+ Grafana
diff --git a/finance/app/templates/index.html b/finance/app/templates/index.html
index 7cf6455..ce27e1f 100644
--- a/finance/app/templates/index.html
+++ b/finance/app/templates/index.html
@@ -77,8 +77,8 @@
Grafana-Dashboard
-
+
Kein Diagramm sichtbar? Einmal in
- Grafana anmelden
+ Grafana anmelden
(gleiches Passwort wie hier).
{% endblock %}
diff --git a/finance/app/templates/szenarien.html b/finance/app/templates/szenarien.html
index 5d910e8..953b1db 100644
--- a/finance/app/templates/szenarien.html
+++ b/finance/app/templates/szenarien.html
@@ -188,7 +188,7 @@
Tiefpunkt: {{ row.result.low_point_balance|eur }} € am {{ row.result.low_point_date.strftime('%d.%m.%Y') }}
{% if row.result.below_zero_date %}Unterschreitet 0 € ab {{ row.result.below_zero_date.strftime('%d.%m.%Y') }}
{% endif %}
{% if row.result.below_threshold_date %}Unterschreitet Warnschwelle ab {{ row.result.below_threshold_date.strftime('%d.%m.%Y') }}
{% endif %}
- Kurven in Grafana ansehen.
+ Kurven in Grafana ansehen.
{% else %}
Noch nicht durchgerechnet.
diff --git a/finance/tests/test_gui.py b/finance/tests/test_gui.py
index fa3bcf4..6809d19 100644
--- a/finance/tests/test_gui.py
+++ b/finance/tests/test_gui.py
@@ -1,6 +1,7 @@
from datetime import date, timedelta
from decimal import Decimal
+from app.config import get_settings
from app.formats import eur
from app.models.tables import (Account, Loan, PlannedItem, RecurringItem,
Scenario, ScenarioModifier, ScenarioPlannedItem)
@@ -453,3 +454,32 @@ def test_neuer_eintrag_formular_struktur(client, db):
assert 'class="value-label"' in r # dynamisches Wert-Label
assert 'Für diese Eintragsart nicht relevant' in r # Tooltip an Umschaltfeldern
assert '