diff --git a/.gitignore b/.gitignore index 587b7e3..3bd738e 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ __pycache__/**/* +__pycache__ +.venv diff --git a/proxy_redirect.py b/proxy_redirect.py index 7e3e537..f009661 100644 --- a/proxy_redirect.py +++ b/proxy_redirect.py @@ -12,6 +12,7 @@ check_health, output_custom_redirect, ) +from logger import debug_log class ProxyRedirectBase: @@ -42,21 +43,25 @@ "body": body, } + redirect_uri = self.context["redirect"] + redirect_status = self.context["redirect_status"] + # 2. Perform health check health_result = check_health( - self.context["skip_health_check"], self.context["backend"], - self.context["redirect"], - self.context["redirect_status"], + self.context["health_check"], + self.context["skip_health_check"], + redirect_uri, + redirect_status, ) - # 3. If the health check dictates a redirect, generate the HTML - if health_result["action"] == "redirect": - return output_custom_redirect(health_result["url"], health_result["status"]) + if health_result is True: + return output_custom_redirect(redirect_uri, redirect_status) # 4. Fallback for errors + debug_log(health_result) return { - "status": health_result.get("status", 500), + "status": 500, "headers": {"Content-Type": "text/plain"}, - "body": health_result.get("body", "Internal Server Error"), + "body": health_result, } diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..2669ef7 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,17 @@ +annotated-doc==0.0.4 +annotated-types==0.7.0 +anyio==4.13.0 +certifi==2026.2.25 +charset-normalizer==3.4.6 +click==8.3.1 +fastapi==0.135.2 +h11==0.16.0 +idna==3.11 +pydantic==2.12.5 +pydantic_core==2.41.5 +requests==2.32.5 +starlette==1.0.0 +typing-inspection==0.4.2 +typing_extensions==4.15.0 +urllib3==2.6.3 +uvicorn==0.42.0 diff --git a/router.py b/router.py index 327792d..ff851b9 100644 --- a/router.py +++ b/router.py @@ -3,18 +3,30 @@ Maps incoming HTTP requests to the FastAPIProxyRedirect class. """ -from fastapi import FastAPI, Request +from fastapi import FastAPI, Request, Response +from logger import debug_log from fast_api import FastAPIProxyRedirect app = FastAPI() +@app.get("/authelia-auth") +async def auth_passthrough(): + """ + Explicitly handle authelia-auth if Nginx accidentally routes here. + Returning a 401 triggers the redirect logic in Nginx properly. + """ + # Redirect https://auth.jasonpoage.com/?rd=$scheme://$host$request_uri; + return Response(status_code=200) + + @app.get("/{full_path:path}") async def proxy_handler(request: Request): """ Catch-all route that passes the request to the proxy logic and returns the response. """ proxy = FastAPIProxyRedirect(request) + return proxy.get_response() diff --git a/utils.py b/utils.py index 3fc7058..83ef13f 100644 --- a/utils.py +++ b/utils.py @@ -18,6 +18,7 @@ auth_redirect = env.get("AUTH_REDIRECT_LOCATION", "") app_redirect = env.get("APP_REDIRECT_LOCATION", "") auth_status = str(env.get("AUTH_STATUS", "")) + health_check = env.get("HEALTH_CHECK", False) skip_health_check = env.get("SKIP_HEALTH_CHECK", False) # Type casting and normalization @@ -48,6 +49,7 @@ "auth_redirect": auth_redirect, "app_redirect": app_redirect, "auth_status": auth_status, + "health_check": health_check, "skip_health_check": bool(skip_health_check), "redirect_status": redirect_status, "request_uri": request_uri, @@ -71,13 +73,17 @@ return (200, "Login error - please try again") debug_log("ERROR: No redirect destination available") - return (500, "Missing redirect parameter.") + return (500, "Error: Missing redirect parameter.") return None def check_health( - skip_health_check: bool, backend: str, redirect: str, redirect_status: int + backend: str, + health_check: str, + skip_health_check: bool, + redirect: str, + redirect_status: int, ) -> Dict[str, Any]: """ Validates backend health and returns a data structure for the caller to handle. @@ -92,50 +98,44 @@ except ValueError: is_valid_url = False - if not skip_health_check and backend != "/health" and not is_valid_url: + # Check for valid backend hostname + if not skip_health_check and health_check != "/health" and not is_valid_url: debug_log(f"ERROR: Invalid backend hostname '{backend}'") - return { - "action": "error", - "status": 500, - "body": f"Invalid backend hostname '{backend}'.", - } + return f"Invalid backend hostname '{backend}'." - if not skip_health_check and backend == "/health": - debug_log(f"Health check endpoint, redirecting to: {redirect}") - return {"action": "redirect", "status": redirect_status, "url": redirect} + # + if not skip_health_check and health_check == "/health": + return True - debug_log(f"Checking backend health: {backend}") + # debug_log(f"Checking backend health: {backend}") - http_code = 0 - error_msg = None + # http_code = 0 + # error_msg = None - try: - # Perform HEAD request (CURLOPT_NOBODY equivalent) - response = requests.head( - backend if "://" in backend else f"http://{backend}", - timeout=2, - allow_redirects=False, - ) - http_code = response.status_code - except requests.RequestException as e: - error_msg = str(e) - debug_log(f"CURL error (Requests): {error_msg}") + # try: + # # Perform HEAD request (CURLOPT_NOBODY equivalent) + # response = requests.head( + # backend if "://" in backend else f"http://{backend}", + # timeout=2, + # allow_redirects=False, + # ) + # http_code = response.status_code + # except requests.RequestException as e: + # error_msg = str(e) + # debug_log(f"CURL error (Requests): {error_msg}") - debug_log(f"Backend response code: {http_code}") + # debug_log(f"Backend response code: {http_code}") - if http_code == 200: - debug_log(f"Backend healthy (code: {http_code}), redirecting to: {redirect}") - return {"action": "redirect", "status": redirect_status, "url": redirect} - else: - debug_log(f"Backend unavailable (code: {http_code}), returning 503") - return { - "action": "error", - "status": 503, - "body": "Backend unavailable.", - } - - # Unconditional redirect (current logic) - return {"action": "redirect", "status": redirect_status, "url": redirect} + # if http_code == 200: + # debug_log(f"Backend healthy (code: {http_code}), redirecting to: {redirect}") + # return {"action": "redirect", "status": redirect_status, "url": redirect} + # else: + # debug_log(f"Backend unavailable (code: {http_code}), returning 503") + # return { + # "action": "error", + # "status": 503, + # "body": "Backend unavailable.", + # } # I don't know what this conditional logic was for... maybe to see if the server was down? # I'll add it back in when it matters @@ -147,6 +147,7 @@ # else: # debug_log(f"Backend unhealthy (code: {http_code}), returning 503") # return {"action": "render", "status": 503, "template": "errors/503.html"} + return True def output_custom_redirect(url: str, status: int) -> Dict[str, Any]: