settings: add nav visibility prefs and per-user Strava credentials

- user_prefs table in db.py with get/set helpers
- GET/PUT /api/me/prefs endpoints for bulk pref management
- GET/PUT/DELETE /api/me/strava-credentials; PUT preserves existing
  secret when client_secret field is left blank
- _strava_creds() helper resolves per-user → instance fallback across
  all five Strava endpoints
- Settings page: Navigation card (hide Feed/Community/About toggles)
  and Strava credentials card
- Base.astro: ids on feed/community/about nav links; applies
  nav_hide_* prefs after login
This commit is contained in:
Davide Scaini
2026-04-15 20:37:42 +02:00
parent 4fd5ba428e
commit 87a69bcc8b
4 changed files with 358 additions and 13 deletions
+38
View File
@@ -59,9 +59,17 @@ CREATE TABLE IF NOT EXISTS settings (
value TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS user_prefs (
handle TEXT NOT NULL REFERENCES users(handle) ON DELETE CASCADE,
key TEXT NOT NULL,
value TEXT NOT NULL,
PRIMARY KEY (handle, key)
);
CREATE INDEX IF NOT EXISTS sessions_handle ON sessions(handle);
CREATE INDEX IF NOT EXISTS invites_created_by ON invites(created_by);
CREATE INDEX IF NOT EXISTS reset_codes_handle ON reset_codes(handle);
CREATE INDEX IF NOT EXISTS user_prefs_handle ON user_prefs(handle);
"""
_SESSION_DAYS = 30
@@ -361,6 +369,36 @@ def create_reset_code(db: sqlite3.Connection, handle: str, created_by: str) -> s
return code
# ── User preferences ─────────────────────────────────────────────────────────
def get_user_prefs(db: sqlite3.Connection, handle: str) -> dict[str, str]:
"""Return all preferences for a user as a plain dict."""
rows = db.execute(
"SELECT key, value FROM user_prefs WHERE handle = ?", (handle,)
).fetchall()
return {r["key"]: r["value"] for r in rows}
def set_user_pref(db: sqlite3.Connection, handle: str, key: str, value: str) -> None:
db.execute(
"INSERT INTO user_prefs (handle, key, value) VALUES (?, ?, ?) "
"ON CONFLICT(handle, key) DO UPDATE SET value = excluded.value",
(handle, key, value),
)
db.commit()
def set_user_prefs(db: sqlite3.Connection, handle: str, prefs: dict[str, str]) -> None:
"""Bulk-upsert multiple preferences for a user."""
for key, value in prefs.items():
db.execute(
"INSERT INTO user_prefs (handle, key, value) VALUES (?, ?, ?) "
"ON CONFLICT(handle, key) DO UPDATE SET value = excluded.value",
(handle, key, value),
)
db.commit()
def use_reset_code(db: sqlite3.Connection, code: str, handle: str) -> bool:
"""Validate a reset code for the given handle and mark it used.