feat: Tandoor integration — settings, test connection, import, duplicate detection
Add TandoorClient (app/tandoor.py) with full recipe creation, image upload, and duplicate detection via the Tandoor REST API. Settings page now has separate Mealie and Tandoor sections. Import page shows both send buttons based on which services are configured. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
+58
-6
@@ -8,6 +8,7 @@ from flask import Flask, render_template, request, redirect, url_for, flash, jso
|
||||
from app import config
|
||||
from app.scraper import scrape
|
||||
from app.mealie import MealieClient
|
||||
from app.tandoor import TandoorClient
|
||||
|
||||
app = Flask(
|
||||
__name__,
|
||||
@@ -28,7 +29,9 @@ VERSION = os.environ.get("VERSION", "dev")
|
||||
def index():
|
||||
"""Redirect to the import page (or settings if not configured)."""
|
||||
cfg = config.load()
|
||||
if not cfg.get("mealie_url") or not cfg.get("mealie_api_key"):
|
||||
has_mealie = bool(cfg.get("mealie_url") and cfg.get("mealie_api_key"))
|
||||
has_tandoor = bool(cfg.get("tandoor_url") and cfg.get("tandoor_api_key"))
|
||||
if not has_mealie and not has_tandoor:
|
||||
return redirect(url_for("settings"))
|
||||
return redirect(url_for("import_page"))
|
||||
|
||||
@@ -41,6 +44,8 @@ def settings():
|
||||
if request.method == "POST":
|
||||
cfg["mealie_url"] = request.form.get("mealie_url", "").strip().rstrip("/")
|
||||
cfg["mealie_api_key"] = request.form.get("mealie_api_key", "").strip()
|
||||
cfg["tandoor_url"] = request.form.get("tandoor_url", "").strip().rstrip("/")
|
||||
cfg["tandoor_api_key"] = request.form.get("tandoor_api_key", "").strip()
|
||||
config.save(cfg)
|
||||
flash("Beállítások mentve.", "success")
|
||||
return redirect(url_for("settings"))
|
||||
@@ -63,14 +68,32 @@ def settings_test():
|
||||
return jsonify({"ok": False, "error": str(exc)})
|
||||
|
||||
|
||||
@app.route("/settings/test-tandoor", methods=["POST"])
|
||||
def settings_test_tandoor():
|
||||
"""AJAX endpoint — test Tandoor connection using form values."""
|
||||
url = (request.form.get("tandoor_url") or "").strip().rstrip("/")
|
||||
key = (request.form.get("tandoor_api_key") or "").strip()
|
||||
if not url or not key:
|
||||
return jsonify({"ok": False, "error": "Nincs megadva Tandoor URL vagy API kulcs."})
|
||||
try:
|
||||
client = TandoorClient(url, key, api_url=config.TANDOOR_INTERNAL_URL)
|
||||
info = client.test_connection()
|
||||
return jsonify({"ok": True, "data": info})
|
||||
except Exception as exc:
|
||||
return jsonify({"ok": False, "error": str(exc)})
|
||||
|
||||
|
||||
@app.route("/import", methods=["GET"])
|
||||
def import_page():
|
||||
"""Show the import form."""
|
||||
cfg = config.load()
|
||||
if not cfg.get("mealie_url") or not cfg.get("mealie_api_key"):
|
||||
flash("Először állítsd be a Mealie kapcsolatot.", "warning")
|
||||
has_mealie = bool(cfg.get("mealie_url") and cfg.get("mealie_api_key"))
|
||||
has_tandoor = bool(cfg.get("tandoor_url") and cfg.get("tandoor_api_key"))
|
||||
if not has_mealie and not has_tandoor:
|
||||
flash("Először állíts be legalább egy szolgáltatást (Mealie vagy Tandoor).", "warning")
|
||||
return redirect(url_for("settings"))
|
||||
return render_template("import.html", cfg=cfg, version=VERSION)
|
||||
return render_template("import.html", cfg=cfg, version=VERSION,
|
||||
has_mealie=has_mealie, has_tandoor=has_tandoor)
|
||||
|
||||
|
||||
@app.route("/scrape", methods=["POST"])
|
||||
@@ -82,8 +105,9 @@ def scrape_url():
|
||||
try:
|
||||
data = scrape(url)
|
||||
|
||||
# Check for duplicate in Mealie
|
||||
# Check for duplicates in Mealie and Tandoor
|
||||
duplicate = None
|
||||
tandoor_duplicate = None
|
||||
cfg = config.load()
|
||||
if cfg.get("mealie_url") and cfg.get("mealie_api_key"):
|
||||
try:
|
||||
@@ -92,8 +116,16 @@ def scrape_url():
|
||||
duplicate = client.find_duplicate(url, data.get("title", ""))
|
||||
except Exception:
|
||||
pass # non-fatal
|
||||
if cfg.get("tandoor_url") and cfg.get("tandoor_api_key"):
|
||||
try:
|
||||
client = TandoorClient(cfg["tandoor_url"], cfg["tandoor_api_key"],
|
||||
api_url=config.TANDOOR_INTERNAL_URL)
|
||||
tandoor_duplicate = client.find_duplicate(url, data.get("title", ""))
|
||||
except Exception:
|
||||
pass # non-fatal
|
||||
|
||||
return jsonify({"ok": True, "data": data, "duplicate": duplicate})
|
||||
return jsonify({"ok": True, "data": data, "duplicate": duplicate,
|
||||
"tandoor_duplicate": tandoor_duplicate})
|
||||
except Exception as exc:
|
||||
return jsonify({"ok": False, "error": str(exc), "trace": traceback.format_exc()})
|
||||
|
||||
@@ -119,6 +151,26 @@ def send_to_mealie():
|
||||
return jsonify({"ok": False, "error": str(exc), "trace": traceback.format_exc()})
|
||||
|
||||
|
||||
@app.route("/send-tandoor", methods=["POST"])
|
||||
def send_to_tandoor():
|
||||
"""AJAX — send edited recipe data to Tandoor."""
|
||||
cfg = config.load()
|
||||
if not cfg.get("tandoor_url") or not cfg.get("tandoor_api_key"):
|
||||
return jsonify({"ok": False, "error": "Tandoor nincs beállítva."})
|
||||
|
||||
payload = request.get_json(silent=True)
|
||||
if not payload:
|
||||
return jsonify({"ok": False, "error": "Érvénytelen kérés."})
|
||||
|
||||
try:
|
||||
client = TandoorClient(cfg["tandoor_url"], cfg["tandoor_api_key"],
|
||||
api_url=config.TANDOOR_INTERNAL_URL)
|
||||
result = client.create_recipe(payload)
|
||||
return jsonify({"ok": True, "id": result["id"], "url": result["url"]})
|
||||
except Exception as exc:
|
||||
return jsonify({"ok": False, "error": str(exc), "trace": traceback.format_exc()})
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Health
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user