From 276647e0792d49f30fbd4b8faeb9a69c6b7ae03c Mon Sep 17 00:00:00 2001 From: Ekaropolus Date: Fri, 19 Sep 2025 00:12:10 -0600 Subject: [PATCH] Total URL rebuilds --- pxy_agents_coral/views.py | 41 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/pxy_agents_coral/views.py b/pxy_agents_coral/views.py index 1545b35..a10e30d 100644 --- a/pxy_agents_coral/views.py +++ b/pxy_agents_coral/views.py @@ -4,6 +4,7 @@ from __future__ import annotations import json import re from typing import Any, Dict, Tuple +from urllib.parse import urlparse import requests from django.conf import settings @@ -11,6 +12,9 @@ from django.http import JsonResponse, HttpRequest from django.views.decorators.csrf import csrf_exempt from django.views.decorators.http import require_http_methods, require_POST +# build absolute public URLs from a request + path +from core.urlbuild import public_url + # ----- contracts version (best-effort) ----- try: from pxy_contracts.version import SPEC_VERSION @@ -24,7 +28,7 @@ AGENTS_INTERNAL_BASE = getattr(settings, "AGENTS_INTERNAL_BASE", "") # For the formatter endpoints we *force* an internal base and never guess from Host. # Set in .env: AGENTS_INTERNAL_BASE=http://127.0.0.1:8002 # Fallback keeps you safe even if env is missing/misread. -FORMAT_INTERNAL_BASE = AGENTS_INTERNAL_BASE or "http://127.0.0.1:8002" +FORMAT_INTERNAL_BASE = (AGENTS_INTERNAL_BASE or "http://127.0.0.1:8002").rstrip("/") # ===== helpers ===== def _load_body(request: HttpRequest) -> Dict[str, Any]: @@ -59,7 +63,7 @@ def _post_underlying(agent: str, payload: Dict[str, Any], timeout: float = 60.0) sites -> /api/sites/search """ path = "/api/sami/run" if agent == "sami" else "/api/sites/search" - url = f"{FORMAT_INTERNAL_BASE.rstrip('/')}{path}" + url = f"{FORMAT_INTERNAL_BASE}{path}" try: r = requests.post(url, json=payload, timeout=timeout) try: @@ -72,6 +76,37 @@ def _post_underlying(agent: str, payload: Dict[str, Any], timeout: float = 60.0) except Exception as e: return 500, {"code": "EXEC_ERROR", "message": str(e), "_debug_url": url} +def _normalize_urls_to_public(data: dict, request: HttpRequest) -> None: + """ + Convert any absolute URLs that may point to 127.0.0.1:8002 into public URLs + using the current request host while preserving the path. + """ + if not isinstance(data, dict): + return + + url_keys = { + # common keys from sami + sites + "share_url", "map_url", "demand_map_url", "competition_map_url", + "main_download_url", "demand_download_url", "competition_download_url", + "main_preview_url", "demand_preview_url", "competition_preview_url", + "isochrones_geojson_url", "candidates_geojson_url", + "pois_competition_geojson_url", "popgrid_geojson_url", + "chart_url", + } + + for k in list(url_keys): + v = data.get(k) + if not isinstance(v, str) or not v: + continue + try: + p = urlparse(v) + # only rewrite absolute http(s) URLs; keep relative ones + if p.scheme in ("http", "https") and p.path: + data[k] = public_url(request, p.path) + except Exception: + # never fail formatting due to a bad URL + pass + # Tiny text builders for bot replies def _text_sami(data: Dict[str, Any]) -> str: if "beta" in data and "r2" in data: @@ -174,6 +209,7 @@ def format_sami(request: HttpRequest): payload, src = _extract_payload(body) status, data = _post_underlying("sami", payload, timeout=60.0) data = data if isinstance(data, dict) else {"result": data} + _normalize_urls_to_public(data, request) # ensure public links data.setdefault("_echo", {"src": src, "payload_keys": list(payload.keys())}) try: data["text"] = _text_sami(data) @@ -188,6 +224,7 @@ def format_sites(request: HttpRequest): payload, src = _extract_payload(body) status, data = _post_underlying("sites", payload, timeout=60.0) data = data if isinstance(data, dict) else {"result": data} + _normalize_urls_to_public(data, request) # ensure public links data.setdefault("_echo", {"src": src, "payload_keys": list(payload.keys())}) try: data["text"] = _text_sites(data)