diff --git a/server/main.py b/server/main.py index 777e11b..d95ebd9 100644 --- a/server/main.py +++ b/server/main.py @@ -146,26 +146,17 @@ def get_server_time(): class DashboardStaticFiles(StaticFiles): """StaticFiles with explicit Cache-Control headers. - Starlette sends only ETag/Last-Modified, so every dashboard load - re-validates all ~9 assets with conditional requests. The HTML shell stays - no-cache (a deploy shows up on the next load); the JS/CSS assets get a - modest max-age so repeat loads within the hour skip the network entirely, - falling back to ETag re-validation once it expires. + Everything is no-cache: the browser keeps its copy but re-validates it + with the ETag on each load, so an unchanged file costs a 304 and a deploy + shows up on the next load. The asset URLs are not versioned, so a max-age + here kept serving the previous release's JS (e.g. a login page without the + SSO button) for up to an hour after an upgrade — longer behind a CDN. """ async def get_response(self, path: str, scope): response = await super().get_response(path, scope) if response.status_code < 400: - # The HTML shell, the service worker and the manifest must revalidate - # every load so a deploy (or a changed SW) is picked up promptly; the - # hashed-in-practice JS/CSS assets get a modest max-age. - if ( - path in ("", ".", "index.html", "sw.js", "manifest.webmanifest") - or path.endswith(".html") - ): - response.headers["Cache-Control"] = "no-cache" - else: - response.headers["Cache-Control"] = "public, max-age=3600" + response.headers["Cache-Control"] = "no-cache" return response diff --git a/server/oidc.py b/server/oidc.py index 4fbf45b..f68d929 100644 --- a/server/oidc.py +++ b/server/oidc.py @@ -48,6 +48,7 @@ OIDC_REQUIRE_EMAIL_VERIFIED, OIDC_SCOPES, PUBLIC_BASE_URL, + SERVER_VERSION, ) logger = logging.getLogger("hivescale.oidc") @@ -124,9 +125,18 @@ def post_logout_redirect_uri(request: Request) -> str: # ── provider HTTP ──────────────────────────────────────────────────────────── +# urllib's default "Python-urllib/3.x" is blocked by Cloudflare's Browser +# Integrity Check (error 1010) when the provider sits behind Cloudflare. +_USER_AGENT = f"HiveHub/{SERVER_VERSION} (OpenID Connect client)" + + def _http_json(url: str, data: Optional[dict] = None, headers: Optional[dict] = None) -> dict: body = urllib.parse.urlencode(data).encode() if data is not None else None - req = urllib.request.Request(url, data=body, headers={"Accept": "application/json", **(headers or {})}) + req = urllib.request.Request( + url, + data=body, + headers={"Accept": "application/json", "User-Agent": _USER_AGENT, **(headers or {})}, + ) try: with urllib.request.urlopen(req, timeout=OIDC_HTTP_TIMEOUT_SECONDS) as resp: return json.loads(resp.read().decode("utf-8"))