Add offline clip caching
This commit is contained in:
79
backend/app/http_cache.py
Normal file
79
backend/app/http_cache.py
Normal file
@@ -0,0 +1,79 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import json
|
||||
from collections.abc import Mapping, Sequence
|
||||
from datetime import datetime
|
||||
from typing import Any
|
||||
|
||||
from fastapi import Request, Response
|
||||
from fastapi.encoders import jsonable_encoder
|
||||
|
||||
|
||||
def _strip_keys(value: Any, excluded_keys: set[str]) -> Any:
|
||||
if isinstance(value, Mapping):
|
||||
return {
|
||||
key: _strip_keys(item, excluded_keys)
|
||||
for key, item in value.items()
|
||||
if key not in excluded_keys
|
||||
}
|
||||
if isinstance(value, Sequence) and not isinstance(value, (str, bytes, bytearray)):
|
||||
return [_strip_keys(item, excluded_keys) for item in value]
|
||||
return value
|
||||
|
||||
|
||||
def build_etag(payload: Any, *, exclude_keys: set[str] | None = None) -> str:
|
||||
encoded = jsonable_encoder(payload)
|
||||
if exclude_keys:
|
||||
encoded = _strip_keys(encoded, exclude_keys)
|
||||
serialized = json.dumps(encoded, sort_keys=True, separators=(",", ":"), ensure_ascii=False)
|
||||
digest = hashlib.sha1(serialized.encode("utf-8")).hexdigest()
|
||||
return f'"{digest}"'
|
||||
|
||||
|
||||
def is_matching_etag(request: Request, etag: str) -> bool:
|
||||
header = request.headers.get("if-none-match")
|
||||
if not header:
|
||||
return False
|
||||
|
||||
for candidate in header.split(","):
|
||||
if candidate.strip() in {etag, "*"}:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def apply_cache_headers(
|
||||
response: Response,
|
||||
*,
|
||||
cache_control: str,
|
||||
etag: str | None = None,
|
||||
last_modified: datetime | None = None,
|
||||
) -> None:
|
||||
response.headers["Cache-Control"] = cache_control
|
||||
response.headers["Vary"] = "Cookie, Authorization"
|
||||
if etag is not None:
|
||||
response.headers["ETag"] = etag
|
||||
if last_modified is not None:
|
||||
response.headers["Last-Modified"] = last_modified.strftime("%a, %d %b %Y %H:%M:%S GMT")
|
||||
|
||||
|
||||
def set_no_store(response: Response) -> None:
|
||||
apply_cache_headers(response, cache_control="no-store")
|
||||
|
||||
|
||||
def set_private_revalidate(response: Response, *, etag: str | None = None, last_modified: datetime | None = None) -> None:
|
||||
apply_cache_headers(
|
||||
response,
|
||||
cache_control="private, max-age=0, must-revalidate",
|
||||
etag=etag,
|
||||
last_modified=last_modified,
|
||||
)
|
||||
|
||||
|
||||
def set_public_immutable(response: Response, *, etag: str | None = None, last_modified: datetime | None = None) -> None:
|
||||
apply_cache_headers(
|
||||
response,
|
||||
cache_control="public, max-age=31536000, immutable",
|
||||
etag=etag,
|
||||
last_modified=last_modified,
|
||||
)
|
||||
Reference in New Issue
Block a user