feat: public sharing links for households
CI / test-and-package (push) Successful in 40s

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:
agentbox
2026-08-01 10:12:21 +00:00
parent 2a3101071b
commit ae95841b5b
7 changed files with 330 additions and 0 deletions
+37
View File
@@ -86,6 +86,10 @@ def init_db() -> None:
db.execute("ALTER TABLE households ADD COLUMN last_lunch_reminder TEXT DEFAULT ''")
if "last_dinner_reminder" not in hh_cols:
db.execute("ALTER TABLE households ADD COLUMN last_dinner_reminder TEXT DEFAULT ''")
if "public_token" not in hh_cols:
db.execute("ALTER TABLE households ADD COLUMN public_token TEXT DEFAULT ''")
if "public_enabled" not in hh_cols:
db.execute("ALTER TABLE households ADD COLUMN public_enabled INTEGER DEFAULT 1")
# ── Households ────────────────────────────────────────────────────────────────
@@ -156,6 +160,39 @@ def update_reminder_sent(household_id: int, meal_type: str, date_str: str) -> No
(date_str, household_id))
# ── Public sharing ───────────────────────────────────────────────────────────
def get_household_by_public_token(token: str) -> dict | None:
"""Look up a household by its public sharing token."""
with get_db() as db:
row = db.execute(
"SELECT * FROM households WHERE public_token = ? AND public_enabled = 1",
(token,),
).fetchone()
return dict(row) if row else None
def generate_public_token(household_id: int) -> str:
"""Generate a new hard-to-guess public token for a household."""
import secrets
token = secrets.token_urlsafe(32)
with get_db() as db:
db.execute(
"UPDATE households SET public_token = ?, public_enabled = 1 WHERE id = ?",
(token, household_id),
)
return token
def set_public_enabled(household_id: int, enabled: bool) -> None:
"""Enable or disable the public sharing link for a household."""
with get_db() as db:
db.execute(
"UPDATE households SET public_enabled = ? WHERE id = ?",
(int(enabled), household_id),
)
# ── Users ─────────────────────────────────────────────────────────────────────
def get_user_by_id(user_id: int) -> dict | None: