-- Profiles: public display info for users (creator names on community decks) CREATE TABLE IF NOT EXISTS omotomo.profiles ( id uuid PRIMARY KEY REFERENCES auth.users(id) ON DELETE CASCADE, display_name text, created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now() ); ALTER TABLE omotomo.profiles ENABLE ROW LEVEL SECURITY; -- Anyone can read profiles (to show creator names) DROP POLICY IF EXISTS profiles_select ON omotomo.profiles; DROP POLICY IF EXISTS profiles_insert ON omotomo.profiles; DROP POLICY IF EXISTS profiles_update ON omotomo.profiles; DROP POLICY IF EXISTS profiles_delete ON omotomo.profiles; CREATE POLICY profiles_select ON omotomo.profiles FOR SELECT USING (true); -- Users can insert/update/delete only their own profile CREATE POLICY profiles_insert ON omotomo.profiles FOR INSERT WITH CHECK (auth.uid() = id); CREATE POLICY profiles_update ON omotomo.profiles FOR UPDATE USING (auth.uid() = id) WITH CHECK (auth.uid() = id); CREATE POLICY profiles_delete ON omotomo.profiles FOR DELETE USING (auth.uid() = id); -- Create profile when a new user signs up (Supabase Auth) CREATE OR REPLACE FUNCTION omotomo.handle_new_user() RETURNS TRIGGER LANGUAGE plpgsql SECURITY DEFINER SET search_path = omotomo, public AS $$ BEGIN INSERT INTO omotomo.profiles (id, display_name) VALUES ( NEW.id, COALESCE( NEW.raw_user_meta_data->>'full_name', NEW.raw_user_meta_data->>'name', split_part(NEW.email, '@', 1), 'User' ) ) ON CONFLICT (id) DO NOTHING; RETURN NEW; END; $$; DROP TRIGGER IF EXISTS on_auth_user_created ON auth.users; CREATE TRIGGER on_auth_user_created AFTER INSERT ON auth.users FOR EACH ROW EXECUTE FUNCTION omotomo.handle_new_user(); -- Backfill existing users: run in Supabase Dashboard SQL Editor (Service role) if you have users already: -- INSERT INTO omotomo.profiles (id, display_name) -- SELECT id, COALESCE(raw_user_meta_data->>'full_name', raw_user_meta_data->>'name', split_part(email, '@', 1), 'User') -- FROM auth.users ON CONFLICT (id) DO NOTHING; -- Deck ratings: one rating (1–5 stars) per user per deck CREATE TABLE IF NOT EXISTS omotomo.deck_ratings ( deck_id uuid NOT NULL REFERENCES omotomo.decks(id) ON DELETE CASCADE, user_id uuid NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE, rating smallint NOT NULL CHECK (rating >= 1 AND rating <= 5), created_at timestamptz NOT NULL DEFAULT now(), PRIMARY KEY (deck_id, user_id) ); CREATE INDEX IF NOT EXISTS idx_deck_ratings_deck_id ON omotomo.deck_ratings(deck_id); ALTER TABLE omotomo.deck_ratings ENABLE ROW LEVEL SECURITY; -- Anyone can read ratings (for showing averages on community) DROP POLICY IF EXISTS deck_ratings_select ON omotomo.deck_ratings; DROP POLICY IF EXISTS deck_ratings_insert ON omotomo.deck_ratings; DROP POLICY IF EXISTS deck_ratings_update ON omotomo.deck_ratings; DROP POLICY IF EXISTS deck_ratings_delete ON omotomo.deck_ratings; CREATE POLICY deck_ratings_select ON omotomo.deck_ratings FOR SELECT USING (true); -- Authenticated users can insert/update only their own rating, and not for decks they own CREATE POLICY deck_ratings_insert ON omotomo.deck_ratings FOR INSERT WITH CHECK ( auth.uid() = user_id AND (SELECT d.owner_id FROM omotomo.decks d WHERE d.id = deck_id) IS DISTINCT FROM auth.uid() ); CREATE POLICY deck_ratings_update ON omotomo.deck_ratings FOR UPDATE USING (auth.uid() = user_id) WITH CHECK ( auth.uid() = user_id AND (SELECT d.owner_id FROM omotomo.decks d WHERE d.id = deck_id) IS DISTINCT FROM auth.uid() ); CREATE POLICY deck_ratings_delete ON omotomo.deck_ratings FOR DELETE USING (auth.uid() = user_id);