Skip to content

openfoodfacts_proxy.routes.common

[docs] module openfoodfacts_proxy.routes.common

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
from fastapi import Request
from fastapi.responses import RedirectResponse

ALL_HTTP_METHODS = ["DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT"]


def get_client_ip(request: Request) -> str:
    """Extract the client IP, respecting X-Forwarded-For and X-Real-IP when present."""
    forwarded = request.headers.get("x-forwarded-for")
    if forwarded:
        return forwarded.split(",")[0].strip()
    real_ip = request.headers.get("x-real-ip")
    if real_ip:
        return real_ip.strip()
    return request.client.host if request.client else "unknown"


def build_upstream_url(request: Request, upstream_path: str | None = None) -> str:
    """Build the upstream URL preserving the incoming query string."""
    settings = request.app.state.settings
    path = upstream_path or request.url.path
    path = path.lstrip("/")
    url = f"{settings.off_base_url.rstrip('/')}/{path}"
    if request.url.query:
        return f"{url}?{request.url.query}"
    return url


def redirect_to_upstream(
    request: Request,
    upstream_path: str | None = None,
    *,
    status_code: int = 307,
) -> RedirectResponse:
    """Return a redirect to the upstream OFF API.

    Redirecting keeps the effective OFF rate limit tied to the client IP instead of
    collapsing all traffic behind this server's IP address.
    """
    return RedirectResponse(
        url=build_upstream_url(request, upstream_path),
        status_code=status_code,
        headers={"X-Proxy-Source": "upstream-redirect"},
    )