From 714aebfdc0e63672d5eb3f4e7793b56aaf5e9b9a Mon Sep 17 00:00:00 2001 From: nessi Date: Thu, 9 Jul 2026 14:30:11 +0200 Subject: [PATCH] feat: add /agents/ nginx proxy route and improve agent heartbeat error handling with URL normalization Add /agents/ location block to nginx config to proxy agent heartbeat requests directly to backend API without /api/v1 prefix duplication, implement normalized_api_url helper to detect and fix double /api/v1 suffixes in agent config with automatic /api/v1 appending when missing, enhance heartbeat error logging to include HTTP status codes, response body preview, and resolved API URL for debugging connection --- backend/app/agent_assets/nexafabric-agent.py | 21 ++++++++++++++++---- nginx/default.conf | 9 +++++++++ 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/backend/app/agent_assets/nexafabric-agent.py b/backend/app/agent_assets/nexafabric-agent.py index 5777f8c..b9e0485 100644 --- a/backend/app/agent_assets/nexafabric-agent.py +++ b/backend/app/agent_assets/nexafabric-agent.py @@ -162,11 +162,21 @@ def collect_payload(config: dict[str, Any]) -> dict[str, Any]: } -def post_heartbeat(config: dict[str, Any], payload: dict[str, Any]) -> None: +def normalized_api_url(config: dict[str, Any]) -> str: api_url = str(config["api_url"]).rstrip("/") + while api_url.endswith("/api/v1/api/v1"): + api_url = api_url.removesuffix("/api/v1") + if not api_url.endswith("/api/v1"): + api_url = f"{api_url}/api/v1" + return api_url + + +def post_heartbeat(config: dict[str, Any], payload: dict[str, Any]) -> None: + api_url = normalized_api_url(config) + heartbeat_url = f"{api_url}/agents/heartbeat" data = json.dumps(payload).encode("utf-8") request = urllib.request.Request( - f"{api_url}/agents/heartbeat", + heartbeat_url, data=data, headers={ "Authorization": f"Bearer {config['token']}", @@ -199,8 +209,11 @@ def main() -> int: try: post_heartbeat(config, payload) print(f"heartbeat ok: {payload['collected_at']}", flush=True) - except (OSError, urllib.error.URLError, urllib.error.HTTPError) as exc: - print(f"heartbeat failed: {exc}", flush=True) + except urllib.error.HTTPError as exc: + body = exc.read().decode("utf-8", errors="replace")[:500] + print(f"heartbeat failed: HTTP {exc.code} {exc.reason} url={exc.url} body={body}", flush=True) + except (OSError, urllib.error.URLError) as exc: + print(f"heartbeat failed: {exc} api_url={normalized_api_url(config)}", flush=True) if args.once: return 0 time.sleep(interval) diff --git a/nginx/default.conf b/nginx/default.conf index f87cc37..d1b32f2 100644 --- a/nginx/default.conf +++ b/nginx/default.conf @@ -11,6 +11,15 @@ server { proxy_set_header X-Forwarded-Proto $scheme; } + location /agents/ { + proxy_pass http://api:8000/api/v1/agents/; + proxy_set_header Host $http_host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Host $http_host; + proxy_set_header X-Forwarded-Proto $scheme; + } + location /healthz { proxy_pass http://api:8000/healthz; }