feat: initial recipe-importer application
Python/Flask web app that scrapes Hungarian recipe sites (mindmegette.hu) and imports them into Mealie via its REST API. Includes dark-themed web UI with editable preview, Dockerfile, build script, and docker-compose. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
+115
@@ -0,0 +1,115 @@
|
||||
"""Flask application — recipe importer web UI."""
|
||||
|
||||
import os
|
||||
import traceback
|
||||
|
||||
from flask import Flask, render_template, request, redirect, url_for, flash, jsonify
|
||||
|
||||
from app import config
|
||||
from app.scraper import scrape
|
||||
from app.mealie import MealieClient
|
||||
|
||||
app = Flask(
|
||||
__name__,
|
||||
template_folder=os.path.join(os.path.dirname(__file__), "templates"),
|
||||
static_folder=os.path.join(os.path.dirname(__file__), "static"),
|
||||
)
|
||||
app.secret_key = os.environ.get("SECRET_KEY", "recipe-importer-dev-key")
|
||||
|
||||
VERSION = os.environ.get("VERSION", "dev")
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Routes
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
@app.route("/")
|
||||
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"):
|
||||
return redirect(url_for("settings"))
|
||||
return redirect(url_for("import_page"))
|
||||
|
||||
|
||||
@app.route("/settings", methods=["GET", "POST"])
|
||||
def settings():
|
||||
"""Configure Mealie connection."""
|
||||
cfg = config.load()
|
||||
|
||||
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()
|
||||
config.save(cfg)
|
||||
flash("Beállítások mentve.", "success")
|
||||
return redirect(url_for("settings"))
|
||||
|
||||
return render_template("settings.html", cfg=cfg, version=VERSION)
|
||||
|
||||
|
||||
@app.route("/settings/test", methods=["POST"])
|
||||
def settings_test():
|
||||
"""AJAX endpoint — test Mealie connection."""
|
||||
cfg = config.load()
|
||||
if not cfg.get("mealie_url") or not cfg.get("mealie_api_key"):
|
||||
return jsonify({"ok": False, "error": "Nincs megadva Mealie URL vagy API kulcs."})
|
||||
try:
|
||||
client = MealieClient(cfg["mealie_url"], cfg["mealie_api_key"])
|
||||
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")
|
||||
return redirect(url_for("settings"))
|
||||
return render_template("import.html", cfg=cfg, version=VERSION)
|
||||
|
||||
|
||||
@app.route("/scrape", methods=["POST"])
|
||||
def scrape_url():
|
||||
"""AJAX — scrape a recipe URL and return structured data."""
|
||||
url = request.form.get("url", "").strip()
|
||||
if not url:
|
||||
return jsonify({"ok": False, "error": "Nincs URL megadva."})
|
||||
try:
|
||||
data = scrape(url)
|
||||
return jsonify({"ok": True, "data": data})
|
||||
except Exception as exc:
|
||||
return jsonify({"ok": False, "error": str(exc), "trace": traceback.format_exc()})
|
||||
|
||||
|
||||
@app.route("/send", methods=["POST"])
|
||||
def send_to_mealie():
|
||||
"""AJAX — send edited recipe data to Mealie."""
|
||||
cfg = config.load()
|
||||
if not cfg.get("mealie_url") or not cfg.get("mealie_api_key"):
|
||||
return jsonify({"ok": False, "error": "Mealie 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 = MealieClient(cfg["mealie_url"], cfg["mealie_api_key"])
|
||||
slug = client.create_recipe(payload)
|
||||
recipe_url = f"{cfg['mealie_url']}/g/home/r/{slug}"
|
||||
return jsonify({"ok": True, "slug": slug, "url": recipe_url})
|
||||
except Exception as exc:
|
||||
return jsonify({"ok": False, "error": str(exc), "trace": traceback.format_exc()})
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Health
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
@app.route("/health")
|
||||
def health():
|
||||
return jsonify({"status": "ok", "version": VERSION})
|
||||
Reference in New Issue
Block a user