Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 6 additions & 15 deletions server/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -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


Expand Down
12 changes: 11 additions & 1 deletion server/oidc.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
OIDC_REQUIRE_EMAIL_VERIFIED,
OIDC_SCOPES,
PUBLIC_BASE_URL,
SERVER_VERSION,
)

logger = logging.getLogger("hivescale.oidc")
Expand Down Expand Up @@ -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"))
Expand Down
Loading