fix: add MEALIE_INTERNAL_URL env var for Docker-to-Docker API calls
When deployed behind Cloudflare Tunnel, requests from the container to the external Mealie URL fail with 530 (hairpin). MEALIE_INTERNAL_URL lets the container use the Docker-internal address (e.g. http://mealie:9000) for API calls while keeping the external URL for browser links. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -7,6 +7,10 @@ from pathlib import Path
|
|||||||
DATA_DIR = Path(os.environ.get("DATA_DIR", "/data"))
|
DATA_DIR = Path(os.environ.get("DATA_DIR", "/data"))
|
||||||
CONFIG_FILE = DATA_DIR / "config.json"
|
CONFIG_FILE = DATA_DIR / "config.json"
|
||||||
|
|
||||||
|
# Optional internal URL for Docker-to-Docker API calls (avoids Cloudflare hairpin).
|
||||||
|
# The user-facing mealie_url is still used for browser links.
|
||||||
|
MEALIE_INTERNAL_URL = os.environ.get("MEALIE_INTERNAL_URL", "").strip().rstrip("/")
|
||||||
|
|
||||||
_DEFAULTS = {
|
_DEFAULTS = {
|
||||||
"mealie_url": "",
|
"mealie_url": "",
|
||||||
"mealie_api_key": "",
|
"mealie_api_key": "",
|
||||||
|
|||||||
+3
-2
@@ -56,7 +56,7 @@ def settings_test():
|
|||||||
if not url or not key:
|
if not url or not key:
|
||||||
return jsonify({"ok": False, "error": "Nincs megadva Mealie URL vagy API kulcs."})
|
return jsonify({"ok": False, "error": "Nincs megadva Mealie URL vagy API kulcs."})
|
||||||
try:
|
try:
|
||||||
client = MealieClient(url, key)
|
client = MealieClient(url, key, api_url=config.MEALIE_INTERNAL_URL)
|
||||||
info = client.test_connection()
|
info = client.test_connection()
|
||||||
return jsonify({"ok": True, "data": info})
|
return jsonify({"ok": True, "data": info})
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
@@ -98,7 +98,8 @@ def send_to_mealie():
|
|||||||
return jsonify({"ok": False, "error": "Érvénytelen kérés."})
|
return jsonify({"ok": False, "error": "Érvénytelen kérés."})
|
||||||
|
|
||||||
try:
|
try:
|
||||||
client = MealieClient(cfg["mealie_url"], cfg["mealie_api_key"])
|
client = MealieClient(cfg["mealie_url"], cfg["mealie_api_key"],
|
||||||
|
api_url=config.MEALIE_INTERNAL_URL)
|
||||||
slug = client.create_recipe(payload)
|
slug = client.create_recipe(payload)
|
||||||
recipe_url = f"{cfg['mealie_url']}/g/home/r/{slug}"
|
recipe_url = f"{cfg['mealie_url']}/g/home/r/{slug}"
|
||||||
return jsonify({"ok": True, "slug": slug, "url": recipe_url})
|
return jsonify({"ok": True, "slug": slug, "url": recipe_url})
|
||||||
|
|||||||
+6
-5
@@ -8,8 +8,9 @@ import requests
|
|||||||
class MealieClient:
|
class MealieClient:
|
||||||
"""Thin wrapper around the Mealie REST API."""
|
"""Thin wrapper around the Mealie REST API."""
|
||||||
|
|
||||||
def __init__(self, base_url: str, api_key: str):
|
def __init__(self, base_url: str, api_key: str, api_url: str = ""):
|
||||||
self.base_url = base_url.rstrip("/")
|
self.base_url = base_url.rstrip("/")
|
||||||
|
self.api_url = api_url.rstrip("/") if api_url else self.base_url
|
||||||
self.session = requests.Session()
|
self.session = requests.Session()
|
||||||
self.session.headers.update({
|
self.session.headers.update({
|
||||||
"Authorization": f"Bearer {api_key}",
|
"Authorization": f"Bearer {api_key}",
|
||||||
@@ -22,7 +23,7 @@ class MealieClient:
|
|||||||
|
|
||||||
def test_connection(self) -> dict:
|
def test_connection(self) -> dict:
|
||||||
"""Return Mealie app info or raise on failure."""
|
"""Return Mealie app info or raise on failure."""
|
||||||
r = self.session.get(f"{self.base_url}/api/app/about", timeout=10)
|
r = self.session.get(f"{self.api_url}/api/app/about", timeout=10)
|
||||||
r.raise_for_status()
|
r.raise_for_status()
|
||||||
return r.json()
|
return r.json()
|
||||||
|
|
||||||
@@ -34,7 +35,7 @@ class MealieClient:
|
|||||||
"""
|
"""
|
||||||
# Step 1: create stub
|
# Step 1: create stub
|
||||||
r = self.session.post(
|
r = self.session.post(
|
||||||
f"{self.base_url}/api/recipes",
|
f"{self.api_url}/api/recipes",
|
||||||
json={"name": recipe["title"]},
|
json={"name": recipe["title"]},
|
||||||
timeout=15,
|
timeout=15,
|
||||||
)
|
)
|
||||||
@@ -44,7 +45,7 @@ class MealieClient:
|
|||||||
# Step 2: build full payload and PATCH
|
# Step 2: build full payload and PATCH
|
||||||
payload = self._build_payload(recipe)
|
payload = self._build_payload(recipe)
|
||||||
r = self.session.patch(
|
r = self.session.patch(
|
||||||
f"{self.base_url}/api/recipes/{slug}",
|
f"{self.api_url}/api/recipes/{slug}",
|
||||||
json=payload,
|
json=payload,
|
||||||
timeout=15,
|
timeout=15,
|
||||||
)
|
)
|
||||||
@@ -107,7 +108,7 @@ class MealieClient:
|
|||||||
"image": (f"recipe.{ext}", io.BytesIO(img_resp.content), content_type),
|
"image": (f"recipe.{ext}", io.BytesIO(img_resp.content), content_type),
|
||||||
}
|
}
|
||||||
r = self.session.put(
|
r = self.session.put(
|
||||||
f"{self.base_url}/api/recipes/{slug}/image",
|
f"{self.api_url}/api/recipes/{slug}/image",
|
||||||
files=files,
|
files=files,
|
||||||
timeout=30,
|
timeout=30,
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user