'use strict'; /** * /api/preferences (Session 49) — per-user onboarding preferences. * * Stored in Supabase auth `user_metadata.preferences` (NO migration / extra * table). Auth-gated by the shared `requireAuth` (req.user = the app `users` * row; req.user.id is the auth user id). Reads/writes via the service client's * admin API. POST is a PARTIAL merge — only the keys present in the body change. * * Shape: { sports: string[], books: string[], weekly_limit: number|null, * onboarding_complete: boolean } */ const express = require('express'); const { requireAuth } = require('../middleware/auth'); const { getSupabaseServiceClient } = require('../utils/supabase'); const router = express.Router(); router.use(requireAuth); const DEFAULTS = { sports: [], books: [], weekly_limit: null, onboarding_complete: false }; const VALID_SPORTS = new Set(['mlb', 'nba', 'wnba', 'soccer']); /** Keep only valid keys/values; only present keys are returned (partial). */ function sanitizePrefs(p = {}) { const out = {}; if (Array.isArray(p.sports)) { out.sports = [...new Set(p.sports.map((s) => String(s).toLowerCase()).filter((s) => VALID_SPORTS.has(s)))]; } if (Array.isArray(p.books)) { out.books = [...new Set(p.books.map((b) => String(b).slice(0, 40)))].slice(0, 12); } if (Object.prototype.hasOwnProperty.call(p, 'weekly_limit')) { const n = Number(p.weekly_limit); out.weekly_limit = p.weekly_limit === null ? null : Number.isFinite(n) ? Math.max(0, Math.min(100000, n)) : null; } if (typeof p.onboarding_complete === 'boolean') out.onboarding_complete = p.onboarding_complete; return out; } async function readPrefs(sb, userId) { const { data, error } = await sb.auth.admin.getUserById(userId); if (error) throw error; const prefs = (data && data.user && data.user.user_metadata && data.user.user_metadata.preferences) || {}; const meta = (data && data.user && data.user.user_metadata) || {}; return { prefs: { ...DEFAULTS, ...prefs }, meta }; } // GET /api/preferences router.get('/', async (req, res) => { try { const sb = getSupabaseServiceClient(); const { prefs } = await readPrefs(sb, req.user.id); return res.json(prefs); } catch (err) { console.error('[preferences/get]', err.message); return res.status(503).json({ error: 'Could not load preferences' }); } }); // POST /api/preferences — partial merge. router.post('/', async (req, res) => { try { const sb = getSupabaseServiceClient(); const { prefs: existing, meta } = await readPrefs(sb, req.user.id); const merged = { ...existing, ...sanitizePrefs(req.body || {}) }; const { error } = await sb.auth.admin.updateUserById(req.user.id, { user_metadata: { ...meta, preferences: merged }, }); if (error) throw error; return res.json({ ok: true, preferences: merged }); } catch (err) { console.error('[preferences/post]', err.message); return res.status(503).json({ error: 'Could not save preferences' }); } }); module.exports = router;