Add hard-to-guess public URLs (/public/<token>) that show a read-only meal status dashboard for each household. Only people with the link can see the data — no login required. - models.py: public_token + public_enabled columns on households, get_household_by_public_token(), generate_public_token(), set_public_enabled() - meals.py: /public/<token> route, /admin/public-link for generate/disable/enable/regenerate - templates/public.html: clean read-only public dashboard - templates/public-not-found.html: 404 for invalid/disabled links - templates/dashboard.html: admin UI with copy/regenerate/disable - static/style.css: public page and URL box styles - i18n.py: 18 new translation keys (en/fr/de)
This commit is contained in:
@@ -30,6 +30,9 @@ from models import (
|
||||
get_all_households,
|
||||
get_users_with_ntfy,
|
||||
update_reminder_sent,
|
||||
get_household_by_public_token,
|
||||
generate_public_token,
|
||||
set_public_enabled,
|
||||
)
|
||||
from i18n import t
|
||||
from ntfy import send_test_notification, send_meal_reminder
|
||||
@@ -369,6 +372,65 @@ def auto_login(token):
|
||||
return redirect(target)
|
||||
|
||||
|
||||
# ── Public sharing ───────────────────────────────────────────────────────────
|
||||
|
||||
@meals_bp.route("/public/<token>")
|
||||
def public_dashboard(token):
|
||||
"""Public, read-only dashboard showing the household's meal status.
|
||||
|
||||
No login required — the token is a hard-to-guess secret. Query params:
|
||||
- date: YYYY-MM-DD (default: today)
|
||||
"""
|
||||
household = get_household_by_public_token(token)
|
||||
if household is None:
|
||||
return render_template("public-not-found.html"), 404
|
||||
|
||||
today = date.today()
|
||||
date_str = request.args.get("date")
|
||||
if date_str:
|
||||
try:
|
||||
today = datetime.strptime(date_str, "%Y-%m-%d").date()
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
data = get_dashboard_data(today, household["id"])
|
||||
return render_template(
|
||||
"public.html",
|
||||
dashboard=data,
|
||||
household=household,
|
||||
viewing_date=today,
|
||||
today=date.today(),
|
||||
prev_date=today - timedelta(days=1),
|
||||
next_date=today + timedelta(days=1),
|
||||
)
|
||||
|
||||
|
||||
@meals_bp.route("/admin/public-link", methods=["POST"])
|
||||
@login_required
|
||||
def admin_public_link():
|
||||
"""Admin enables/disables or regenerates the public sharing link."""
|
||||
if not session["is_admin"]:
|
||||
flash(t("Only admins can manage the public link."), "error")
|
||||
return redirect(url_for("meals.dashboard"))
|
||||
|
||||
action = request.form.get("action", "").strip()
|
||||
household_id = session["household_id"]
|
||||
|
||||
if action == "generate":
|
||||
token = generate_public_token(household_id)
|
||||
flash(t("Public link generated!"), "success")
|
||||
elif action == "disable":
|
||||
set_public_enabled(household_id, False)
|
||||
flash(t("Public link disabled."), "success")
|
||||
elif action == "enable":
|
||||
set_public_enabled(household_id, True)
|
||||
flash(t("Public link enabled."), "success")
|
||||
else:
|
||||
flash(t("Unknown action."), "error")
|
||||
|
||||
return redirect(url_for("meals.dashboard"))
|
||||
|
||||
|
||||
# ── Cron tick (called by meal-cron container) ─────────────────────────────────
|
||||
|
||||
REMINDER_HOURS = {"lunch": 10, "dinner": 16}
|
||||
|
||||
Reference in New Issue
Block a user