Files
vyndr/supabase/migrations/022_public_profiles.sql
2026-07-11 19:32:22 -04:00

36 lines
1.6 KiB
SQL

-- ---------------------------------------------------------------
-- 022 — public_profiles (A1 Session 10, public ledger profiles v1).
--
-- Stage-3 seed: a user may claim a handle and publish their ENTIRE settled
-- ledger record on a public page — wins and misses. Strava for betting.
--
-- PRIVACY: PRIVATE BY DEFAULT. `published` defaults false and only flips
-- via one explicit toggle in Settings. The profile API returns the SAME
-- 404 for an unknown handle and an unpublished one (no existence leak).
--
-- All writes go through the service role (the /api/profiles/me route).
-- Clients only read: published rows (anyone) + their own row (owner).
-- ---------------------------------------------------------------
CREATE TABLE IF NOT EXISTS public.public_profiles (
user_id uuid PRIMARY KEY REFERENCES auth.users (id) ON DELETE CASCADE,
handle text UNIQUE NOT NULL CHECK (handle ~ '^[a-z0-9_]{3,20}$'),
published boolean NOT NULL DEFAULT false,
created_at timestamptz NOT NULL DEFAULT now()
);
ALTER TABLE public.public_profiles ENABLE ROW LEVEL SECURITY;
-- Anyone (anon + authenticated) reads PUBLISHED profiles only.
CREATE POLICY public_profiles_select_published ON public.public_profiles
FOR SELECT TO anon, authenticated
USING (published = true);
-- The owner reads their own row (published or not).
CREATE POLICY public_profiles_select_own ON public.public_profiles
FOR SELECT TO authenticated
USING (user_id = auth.uid());
-- No INSERT/UPDATE/DELETE policies: client-side writes are impossible.
-- The API writes via the service role, which bypasses RLS.