From a3297840638eaca5aabd9109ddfb3a06ddc8e8b1 Mon Sep 17 00:00:00 2001 From: Kayos Date: Tue, 28 Apr 2026 20:26:25 -0700 Subject: [PATCH] fix: split MEALIE_API_URL (internal) from MEALIE_PUBLIC_URL (UI link) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cauldron's container can't resolve 'recipes.sulkta.com' from inside the sulkta+sulkta-db-net bridges (Lucy's split-horizon doesn't propagate to container DNS). Symptom: 500 on /connect-mealie POST when validating the pasted token. Fix: take the LAN-internal HTTP path direct to mealie. Mealie shares OpenVPN-rack2's netns, listening on 9000 inside that netns. Both cauldron and OpenVPN-rack2 are on sulkta-db-net (172.30.1.0/24), so cauldron talks to 'http://OpenVPN-rack2:9000' via Docker's internal DNS — bypasses Apache/HTTPS termination on Rackham entirely. The public URL stays in the UI (so the connect-mealie page deep-link to mint a token still goes to https://recipes.sulkta.com via the user's browser, which DOES resolve it). Also tightened Mealie._get/_put/_post to wrap requests.RequestException into MealieError so connection failures don't 500 callers. --- cauldron/config.py | 10 ++++++++-- cauldron/mealie.py | 15 ++++++++++++--- cauldron/server.py | 8 ++++---- 3 files changed, 24 insertions(+), 9 deletions(-) diff --git a/cauldron/config.py b/cauldron/config.py index edd4141..83c6344 100644 --- a/cauldron/config.py +++ b/cauldron/config.py @@ -9,7 +9,8 @@ class Config: bind_host: str bind_port: int - mealie_base_url: str + mealie_api_url: str # internal URL cauldron uses for HTTP calls (LAN-internal) + mealie_public_url: str # external URL shown to users for token-mint UI mealie_api_token: str # system token (Cobb's "Cauldron" token, used for admin batch ops) clawdforge_url: str @@ -41,7 +42,12 @@ def load() -> Config: secret_key=os.environ["SECRET_KEY"], bind_host=os.environ.get("BIND_HOST", "0.0.0.0"), bind_port=int(os.environ.get("BIND_PORT", "7790")), - mealie_base_url=os.environ["MEALIE_BASE_URL"].rstrip("/"), + mealie_api_url=os.environ.get( + "MEALIE_API_URL", os.environ["MEALIE_BASE_URL"] + ).rstrip("/"), + mealie_public_url=os.environ.get( + "MEALIE_PUBLIC_URL", os.environ["MEALIE_BASE_URL"] + ).rstrip("/"), mealie_api_token=os.environ["MEALIE_API_TOKEN"], clawdforge_url=os.environ["CLAWDFORGE_URL"].rstrip("/"), clawdforge_token=os.environ["CLAWDFORGE_TOKEN"], diff --git a/cauldron/mealie.py b/cauldron/mealie.py index 1f32fa3..ff036e3 100644 --- a/cauldron/mealie.py +++ b/cauldron/mealie.py @@ -35,19 +35,28 @@ class Mealie: # --- low-level ---------------------------------------------------------- def _get(self, path: str, **params) -> dict: - r = self.session.get(f"{self.base_url}{path}", params=params, timeout=30) + try: + r = self.session.get(f"{self.base_url}{path}", params=params, timeout=30) + except requests.RequestException as e: + raise MealieError(f"GET {path} transport: {e}") from e if r.status_code >= 400: raise MealieError(f"GET {path} -> {r.status_code}: {r.text[:300]}") return r.json() def _put(self, path: str, body: dict) -> dict: - r = self.session.put(f"{self.base_url}{path}", json=body, timeout=30) + try: + r = self.session.put(f"{self.base_url}{path}", json=body, timeout=30) + except requests.RequestException as e: + raise MealieError(f"PUT {path} transport: {e}") from e if r.status_code >= 400: raise MealieError(f"PUT {path} -> {r.status_code}: {r.text[:300]}") return r.json() def _post(self, path: str, body: dict) -> dict: - r = self.session.post(f"{self.base_url}{path}", json=body, timeout=30) + try: + r = self.session.post(f"{self.base_url}{path}", json=body, timeout=30) + except requests.RequestException as e: + raise MealieError(f"POST {path} transport: {e}") from e if r.status_code >= 400: raise MealieError(f"POST {path} -> {r.status_code}: {r.text[:300]}") return r.json() diff --git a/cauldron/server.py b/cauldron/server.py index 96c8a5a..e99d052 100644 --- a/cauldron/server.py +++ b/cauldron/server.py @@ -44,7 +44,7 @@ forge = Forge( default_timeout=cfg.default_timeout_secs, ) # System-tier Mealie client (Cobb's "Cauldron" token; admin batch ops only) -system_mealie = Mealie(base_url=cfg.mealie_base_url, api_token=cfg.mealie_api_token) +system_mealie = Mealie(base_url=cfg.mealie_api_url, api_token=cfg.mealie_api_token) system_sterilizer = Sterilizer(mealie=system_mealie, forge=forge, model=cfg.default_model) @@ -103,7 +103,7 @@ def create_app() -> Flask: tok = crypto.decrypt(blob) except Exception: return None - return Mealie(base_url=cfg.mealie_base_url, api_token=tok) + return Mealie(base_url=cfg.mealie_api_url, api_token=tok) # ---------- public --------------------------------------------------- @@ -188,7 +188,7 @@ def create_app() -> Flask: return render_template_string( CONNECT_TEMPLATE, user=u, - mealie_url=cfg.mealie_base_url, + mealie_url=cfg.mealie_public_url, css=_PALETTE_CSS, ) @@ -201,7 +201,7 @@ def create_app() -> Flask: return ("empty token", 400) # Validate against Mealie before storing — don't persist a bad token - test = Mealie(base_url=cfg.mealie_base_url, api_token=token) + test = Mealie(base_url=cfg.mealie_api_url, api_token=token) try: test.who_am_i() except MealieError as e: