Add ntfy push notifications with action buttons for meal reminders
CI / test-and-package (push) Successful in 37s
CI / test-and-package (push) Successful in 37s
- ntfy.py: send_meal_reminder() with Home/Out HTTP action buttons, test helper - models.py: new columns ntfy_url, ntfy_token, callback_token on users table - meals.py: /api/ntfy-callback (public, token-authenticated), /ntfy-settings, /ntfy-test - app.py: flask send-reminders CLI command (cron-schedulable) - Dashboard: ntfy config form in Account Settings - i18n: translations for all ntfy strings (en/fr/de) - Dockerfile: include ntfy.py in COPY - tests: 15 new tests covering callback, settings, and auth - docs: updated AGENTS.md and doc/index.md
This commit is contained in:
@@ -69,6 +69,12 @@ def init_db() -> None:
|
||||
if "household_id" not in cols:
|
||||
db.execute("ALTER TABLE users ADD COLUMN household_id INTEGER REFERENCES households(id)")
|
||||
db.execute("CREATE INDEX IF NOT EXISTS idx_users_household ON users(household_id)")
|
||||
if "ntfy_url" not in cols:
|
||||
db.execute("ALTER TABLE users ADD COLUMN ntfy_url TEXT DEFAULT ''")
|
||||
if "ntfy_token" not in cols:
|
||||
db.execute("ALTER TABLE users ADD COLUMN ntfy_token TEXT DEFAULT ''")
|
||||
if "callback_token" not in cols:
|
||||
db.execute("ALTER TABLE users ADD COLUMN callback_token TEXT DEFAULT ''")
|
||||
|
||||
|
||||
# ── Households ────────────────────────────────────────────────────────────────
|
||||
@@ -174,6 +180,52 @@ def get_household_users(household_id: int) -> list[dict]:
|
||||
return [dict(r) for r in rows]
|
||||
|
||||
|
||||
# ── Ntfy / notifications ─────────────────────────────────────────────────────
|
||||
|
||||
def set_user_ntfy(user_id: int, ntfy_url: str, ntfy_token: str,
|
||||
callback_token: str) -> None:
|
||||
"""Save ntfy settings for a user."""
|
||||
with get_db() as db:
|
||||
db.execute(
|
||||
"UPDATE users SET ntfy_url = ?, ntfy_token = ?, callback_token = ? "
|
||||
"WHERE id = ?",
|
||||
(ntfy_url, ntfy_token, callback_token, user_id),
|
||||
)
|
||||
|
||||
|
||||
def get_user_ntfy(user_id: int) -> dict:
|
||||
"""Get ntfy settings for a user."""
|
||||
with get_db() as db:
|
||||
row = db.execute(
|
||||
"SELECT ntfy_url, ntfy_token, callback_token FROM users WHERE id = ?",
|
||||
(user_id,),
|
||||
).fetchone()
|
||||
return dict(row) if row else {}
|
||||
|
||||
|
||||
def get_user_by_callback_token(token: str) -> dict | None:
|
||||
"""Look up a user by their ntfy callback token."""
|
||||
with get_db() as db:
|
||||
row = db.execute(
|
||||
"SELECT id, username, household_id FROM users WHERE callback_token = ?",
|
||||
(token,),
|
||||
).fetchone()
|
||||
return dict(row) if row else None
|
||||
|
||||
|
||||
def get_users_with_ntfy(household_id: int) -> list[dict]:
|
||||
"""Return all users in a household who have ntfy configured."""
|
||||
with get_db() as db:
|
||||
rows = db.execute(
|
||||
"SELECT id, username, ntfy_url, ntfy_token, callback_token "
|
||||
"FROM users WHERE household_id = ? "
|
||||
"AND ntfy_url != '' AND callback_token != '' "
|
||||
"ORDER BY username",
|
||||
(household_id,),
|
||||
).fetchall()
|
||||
return [dict(r) for r in rows]
|
||||
|
||||
|
||||
# ── Meal periods ──────────────────────────────────────────────────────────────
|
||||
|
||||
def ensure_meal_periods(for_date: date) -> list[dict]:
|
||||
|
||||
Reference in New Issue
Block a user