From fdeaf04280355e35d08abb59e7ac316664f61be3 Mon Sep 17 00:00:00 2001 From: elpatron Date: Fri, 19 Jun 2026 16:27:20 +0200 Subject: [PATCH] Add site footer and fix proxy-aware viewer URLs. Build public links from host_url netloc behind reverse proxies so generated viewer URLs do not include the internal upstream port. Co-authored-by: Cursor --- security.py | 18 ++++++++++++++---- static/style.css | 30 ++++++++++++++++++++++++++++++ templates/_footer.html | 3 +++ templates/index.html | 1 + templates/landing.html | 1 + 5 files changed, 49 insertions(+), 4 deletions(-) create mode 100644 templates/_footer.html diff --git a/security.py b/security.py index f3012eb..5ab9d46 100644 --- a/security.py +++ b/security.py @@ -4,6 +4,8 @@ from __future__ import annotations import os +from urllib.parse import urlparse + from flask import Flask, request from flask_limiter import Limiter from flask_limiter.util import get_remote_address @@ -34,6 +36,7 @@ def configure_app(flask_app: Flask) -> None: x_for=1, x_proto=1, x_host=1, + x_port=1, ) limiter.init_app(flask_app) @@ -56,7 +59,14 @@ def configure_app(flask_app: Flask) -> None: def external_base_url() -> str: """Build public base URL (respects reverse proxy and PREFERRED_URL_SCHEME).""" - preferred = os.environ.get("PREFERRED_URL_SCHEME", "").strip() - if preferred: - return f"{preferred}://{request.host}" - return request.host_url.rstrip("/") + host_url = request.host_url.rstrip("/") + preferred = os.environ.get("PREFERRED_URL_SCHEME", "").strip().lower() + if not preferred: + return host_url + + # Use netloc from host_url (honours ProxyFix / X-Forwarded-Host), not request.host + # alone, which can still include the internal upstream port behind a reverse proxy. + netloc = urlparse(host_url).netloc + if not netloc: + netloc = request.host + return f"{preferred}://{netloc}" diff --git a/static/style.css b/static/style.css index 2d7ac90..cc840e5 100644 --- a/static/style.css +++ b/static/style.css @@ -601,6 +601,7 @@ tr:hover td { background: var(--bg-hover); } .landing-page { min-height: 100vh; display: flex; + flex-direction: column; align-items: center; justify-content: center; padding: 24px; @@ -661,6 +662,35 @@ tr:hover td { background: var(--bg-hover); } .landing-lang { margin-top: 8px; } +.site-footer { + text-align: center; + padding: 16px 24px 20px; + font-size: 0.78rem; + color: var(--text-muted); + border-top: 1px solid var(--border); +} + +.site-footer p { + margin: 0; +} + +.site-footer a { + color: var(--accent); + text-decoration: none; +} + +.site-footer a:hover { + text-decoration: underline; +} + +.landing-page .site-footer { + width: 100%; + max-width: 520px; + background: transparent; + border-top: none; + padding-top: 0; +} + /* Viewer link banner */ .viewer-banner { display: flex; diff --git a/templates/_footer.html b/templates/_footer.html new file mode 100644 index 0000000..b9345d1 --- /dev/null +++ b/templates/_footer.html @@ -0,0 +1,3 @@ + diff --git a/templates/index.html b/templates/index.html index 41847be..7d3dc5d 100644 --- a/templates/index.html +++ b/templates/index.html @@ -73,5 +73,6 @@
+ {% include '_footer.html' %} diff --git a/templates/landing.html b/templates/landing.html index b984f95..a40c784 100644 --- a/templates/landing.html +++ b/templates/landing.html @@ -52,5 +52,6 @@ + {% include '_footer.html' %}