Deck ratings (comment + popup), remove Published badge from My decks
This commit is contained in:
@@ -1,5 +1,8 @@
|
||||
-- Schema for app tables (Supabase API uses db.schema: 'omotomo' in config)
|
||||
CREATE SCHEMA IF NOT EXISTS omotomo;
|
||||
|
||||
-- Decks table (metadata + config for Decky quiz/flashcard app)
|
||||
CREATE TABLE IF NOT EXISTS public.decks (
|
||||
CREATE TABLE IF NOT EXISTS omotomo.decks (
|
||||
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
owner_id uuid NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
|
||||
title text NOT NULL,
|
||||
@@ -21,9 +24,9 @@ CREATE TABLE IF NOT EXISTS public.decks (
|
||||
);
|
||||
|
||||
-- Questions table (deck content)
|
||||
CREATE TABLE IF NOT EXISTS public.questions (
|
||||
CREATE TABLE IF NOT EXISTS omotomo.questions (
|
||||
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
deck_id uuid NOT NULL REFERENCES public.decks(id) ON DELETE CASCADE,
|
||||
deck_id uuid NOT NULL REFERENCES omotomo.decks(id) ON DELETE CASCADE,
|
||||
sort_order int NOT NULL DEFAULT 0,
|
||||
prompt text NOT NULL,
|
||||
explanation text,
|
||||
@@ -31,12 +34,12 @@ CREATE TABLE IF NOT EXISTS public.questions (
|
||||
correct_answer_indices jsonb NOT NULL DEFAULT '[]'::jsonb
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_questions_deck_id ON public.questions(deck_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_decks_owner_id ON public.decks(owner_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_decks_published ON public.decks(published) WHERE published = true;
|
||||
CREATE INDEX IF NOT EXISTS idx_questions_deck_id ON omotomo.questions(deck_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_decks_owner_id ON omotomo.decks(owner_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_decks_published ON omotomo.decks(published) WHERE published = true;
|
||||
|
||||
-- updated_at trigger for decks
|
||||
CREATE OR REPLACE FUNCTION public.set_decks_updated_at()
|
||||
CREATE OR REPLACE FUNCTION omotomo.set_decks_updated_at()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
NEW.updated_at = now();
|
||||
@@ -44,74 +47,82 @@ BEGIN
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
DROP TRIGGER IF EXISTS decks_updated_at ON public.decks;
|
||||
DROP TRIGGER IF EXISTS decks_updated_at ON omotomo.decks;
|
||||
CREATE TRIGGER decks_updated_at
|
||||
BEFORE UPDATE ON public.decks
|
||||
BEFORE UPDATE ON omotomo.decks
|
||||
FOR EACH ROW
|
||||
EXECUTE PROCEDURE public.set_decks_updated_at();
|
||||
EXECUTE PROCEDURE omotomo.set_decks_updated_at();
|
||||
|
||||
-- RLS
|
||||
ALTER TABLE public.decks ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE public.questions ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE omotomo.decks ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE omotomo.questions ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
-- decks: SELECT if owner or published; mutate only if owner
|
||||
CREATE POLICY decks_select ON public.decks
|
||||
DROP POLICY IF EXISTS decks_select ON omotomo.decks;
|
||||
DROP POLICY IF EXISTS decks_insert ON omotomo.decks;
|
||||
DROP POLICY IF EXISTS decks_update ON omotomo.decks;
|
||||
DROP POLICY IF EXISTS decks_delete ON omotomo.decks;
|
||||
CREATE POLICY decks_select ON omotomo.decks
|
||||
FOR SELECT
|
||||
USING (owner_id = auth.uid() OR published = true);
|
||||
|
||||
CREATE POLICY decks_insert ON public.decks
|
||||
CREATE POLICY decks_insert ON omotomo.decks
|
||||
FOR INSERT
|
||||
WITH CHECK (owner_id = auth.uid());
|
||||
|
||||
CREATE POLICY decks_update ON public.decks
|
||||
CREATE POLICY decks_update ON omotomo.decks
|
||||
FOR UPDATE
|
||||
USING (owner_id = auth.uid())
|
||||
WITH CHECK (owner_id = auth.uid());
|
||||
|
||||
CREATE POLICY decks_delete ON public.decks
|
||||
CREATE POLICY decks_delete ON omotomo.decks
|
||||
FOR DELETE
|
||||
USING (owner_id = auth.uid());
|
||||
|
||||
-- questions: SELECT if deck is visible; mutate only if deck is owned
|
||||
CREATE POLICY questions_select ON public.questions
|
||||
DROP POLICY IF EXISTS questions_select ON omotomo.questions;
|
||||
DROP POLICY IF EXISTS questions_insert ON omotomo.questions;
|
||||
DROP POLICY IF EXISTS questions_update ON omotomo.questions;
|
||||
DROP POLICY IF EXISTS questions_delete ON omotomo.questions;
|
||||
CREATE POLICY questions_select ON omotomo.questions
|
||||
FOR SELECT
|
||||
USING (
|
||||
EXISTS (
|
||||
SELECT 1 FROM public.decks d
|
||||
SELECT 1 FROM omotomo.decks d
|
||||
WHERE d.id = questions.deck_id
|
||||
AND (d.owner_id = auth.uid() OR d.published = true)
|
||||
)
|
||||
);
|
||||
|
||||
CREATE POLICY questions_insert ON public.questions
|
||||
CREATE POLICY questions_insert ON omotomo.questions
|
||||
FOR INSERT
|
||||
WITH CHECK (
|
||||
EXISTS (
|
||||
SELECT 1 FROM public.decks d
|
||||
SELECT 1 FROM omotomo.decks d
|
||||
WHERE d.id = questions.deck_id AND d.owner_id = auth.uid()
|
||||
)
|
||||
);
|
||||
|
||||
CREATE POLICY questions_update ON public.questions
|
||||
CREATE POLICY questions_update ON omotomo.questions
|
||||
FOR UPDATE
|
||||
USING (
|
||||
EXISTS (
|
||||
SELECT 1 FROM public.decks d
|
||||
SELECT 1 FROM omotomo.decks d
|
||||
WHERE d.id = questions.deck_id AND d.owner_id = auth.uid()
|
||||
)
|
||||
)
|
||||
WITH CHECK (
|
||||
EXISTS (
|
||||
SELECT 1 FROM public.decks d
|
||||
SELECT 1 FROM omotomo.decks d
|
||||
WHERE d.id = questions.deck_id AND d.owner_id = auth.uid()
|
||||
)
|
||||
);
|
||||
|
||||
CREATE POLICY questions_delete ON public.questions
|
||||
CREATE POLICY questions_delete ON omotomo.questions
|
||||
FOR DELETE
|
||||
USING (
|
||||
EXISTS (
|
||||
SELECT 1 FROM public.decks d
|
||||
SELECT 1 FROM omotomo.decks d
|
||||
WHERE d.id = questions.deck_id AND d.owner_id = auth.uid()
|
||||
)
|
||||
);
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
-- 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);
|
||||
@@ -0,0 +1,56 @@
|
||||
-- Add avatar_url to profiles (schema omotomo)
|
||||
ALTER TABLE omotomo.profiles
|
||||
ADD COLUMN IF NOT EXISTS avatar_url text;
|
||||
|
||||
-- Storage bucket for avatars (public read; uploads scoped to user folder).
|
||||
-- If the INSERT fails (e.g. different storage schema), create the bucket in Supabase Dashboard:
|
||||
-- Storage → New bucket → name "avatars", Public bucket ON, File size limit 2MB, Allowed MIME types: image/jpeg, image/png, image/gif, image/webp
|
||||
INSERT INTO storage.buckets (id, name, public, file_size_limit, allowed_mime_types)
|
||||
VALUES (
|
||||
'avatars',
|
||||
'avatars',
|
||||
true,
|
||||
2097152,
|
||||
ARRAY['image/jpeg', 'image/png', 'image/gif', 'image/webp']
|
||||
)
|
||||
ON CONFLICT (id) DO NOTHING;
|
||||
|
||||
-- RLS: allow authenticated users to upload only to their own folder (avatars/<user_id>/...)
|
||||
-- Use auth.jwt()->>'sub' to match the folder name (JWT sub is the user id string).
|
||||
CREATE POLICY "Users can upload own avatar"
|
||||
ON storage.objects
|
||||
FOR INSERT
|
||||
TO authenticated
|
||||
WITH CHECK (
|
||||
bucket_id = 'avatars'
|
||||
AND (storage.foldername(name))[1] = (auth.jwt()->>'sub')
|
||||
);
|
||||
|
||||
CREATE POLICY "Users can update own avatar"
|
||||
ON storage.objects
|
||||
FOR UPDATE
|
||||
TO authenticated
|
||||
USING (
|
||||
bucket_id = 'avatars'
|
||||
AND (storage.foldername(name))[1] = (auth.jwt()->>'sub')
|
||||
)
|
||||
WITH CHECK (
|
||||
bucket_id = 'avatars'
|
||||
AND (storage.foldername(name))[1] = (auth.jwt()->>'sub')
|
||||
);
|
||||
|
||||
CREATE POLICY "Users can delete own avatar"
|
||||
ON storage.objects
|
||||
FOR DELETE
|
||||
TO authenticated
|
||||
USING (
|
||||
bucket_id = 'avatars'
|
||||
AND (storage.foldername(name))[1] = (auth.jwt()->>'sub')
|
||||
);
|
||||
|
||||
-- Public read for avatars (bucket is public)
|
||||
CREATE POLICY "Avatar images are publicly readable"
|
||||
ON storage.objects
|
||||
FOR SELECT
|
||||
TO public
|
||||
USING (bucket_id = 'avatars');
|
||||
@@ -0,0 +1,3 @@
|
||||
-- Mark decks that were added from the community (no Publish in UI)
|
||||
ALTER TABLE omotomo.decks
|
||||
ADD COLUMN IF NOT EXISTS copied_from_deck_id uuid NULL;
|
||||
@@ -0,0 +1,29 @@
|
||||
-- Add optional comment to deck ratings (schema omotomo).
|
||||
-- If deck_ratings doesn't exist (earlier migration not run), create the full table including comment.
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (SELECT 1 FROM pg_tables WHERE schemaname = 'omotomo' AND tablename = 'deck_ratings') THEN
|
||||
CREATE TABLE 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(),
|
||||
comment text,
|
||||
PRIMARY KEY (deck_id, user_id)
|
||||
);
|
||||
CREATE INDEX idx_deck_ratings_deck_id ON omotomo.deck_ratings(deck_id);
|
||||
ALTER TABLE omotomo.deck_ratings ENABLE ROW LEVEL SECURITY;
|
||||
CREATE POLICY deck_ratings_select ON omotomo.deck_ratings FOR SELECT USING (true);
|
||||
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);
|
||||
ELSE
|
||||
ALTER TABLE omotomo.deck_ratings ADD COLUMN IF NOT EXISTS comment text;
|
||||
END IF;
|
||||
END $$;
|
||||
@@ -0,0 +1,10 @@
|
||||
-- Run this in Supabase SQL Editor if you get "column profiles.avatar_url does not exist"
|
||||
-- Use the schema where your profiles table lives (omotomo or public).
|
||||
|
||||
-- If your app uses schema omotomo (db: { schema: 'omotomo' }):
|
||||
ALTER TABLE omotomo.profiles
|
||||
ADD COLUMN IF NOT EXISTS avatar_url text;
|
||||
|
||||
-- If your profiles table is in public schema instead, run this instead:
|
||||
-- ALTER TABLE public.profiles
|
||||
-- ADD COLUMN IF NOT EXISTS avatar_url text;
|
||||
@@ -0,0 +1,5 @@
|
||||
-- Run this in Supabase SQL Editor if you get "column decks.copied_from_deck_id does not exist"
|
||||
-- Uses schema omotomo (match your app's db.schema).
|
||||
|
||||
ALTER TABLE omotomo.decks
|
||||
ADD COLUMN IF NOT EXISTS copied_from_deck_id uuid NULL;
|
||||
@@ -0,0 +1,37 @@
|
||||
-- Run this in Supabase SQL Editor if you get "Bucket not found" when saving profile with avatar.
|
||||
-- If the INSERT fails (e.g. "relation storage.buckets does not exist" or column errors),
|
||||
-- create the bucket in Dashboard: Storage → New bucket → name "avatars", Public ON.
|
||||
|
||||
-- 1) Create the bucket
|
||||
INSERT INTO storage.buckets (id, name, public, file_size_limit, allowed_mime_types)
|
||||
VALUES (
|
||||
'avatars',
|
||||
'avatars',
|
||||
true,
|
||||
2097152,
|
||||
ARRAY['image/jpeg', 'image/png', 'image/gif', 'image/webp']
|
||||
)
|
||||
ON CONFLICT (id) DO NOTHING;
|
||||
|
||||
-- 2) RLS policies so users can upload to their own folder and everyone can read
|
||||
DROP POLICY IF EXISTS "Users can upload own avatar" ON storage.objects;
|
||||
DROP POLICY IF EXISTS "Users can update own avatar" ON storage.objects;
|
||||
DROP POLICY IF EXISTS "Users can delete own avatar" ON storage.objects;
|
||||
DROP POLICY IF EXISTS "Avatar images are publicly readable" ON storage.objects;
|
||||
|
||||
CREATE POLICY "Users can upload own avatar"
|
||||
ON storage.objects FOR INSERT TO authenticated
|
||||
WITH CHECK (bucket_id = 'avatars' AND (storage.foldername(name))[1] = (auth.jwt()->>'sub'));
|
||||
|
||||
CREATE POLICY "Users can update own avatar"
|
||||
ON storage.objects FOR UPDATE TO authenticated
|
||||
USING (bucket_id = 'avatars' AND (storage.foldername(name))[1] = (auth.jwt()->>'sub'))
|
||||
WITH CHECK (bucket_id = 'avatars' AND (storage.foldername(name))[1] = (auth.jwt()->>'sub'));
|
||||
|
||||
CREATE POLICY "Users can delete own avatar"
|
||||
ON storage.objects FOR DELETE TO authenticated
|
||||
USING (bucket_id = 'avatars' AND (storage.foldername(name))[1] = (auth.jwt()->>'sub'));
|
||||
|
||||
CREATE POLICY "Avatar images are publicly readable"
|
||||
ON storage.objects FOR SELECT TO public
|
||||
USING (bucket_id = 'avatars');
|
||||
@@ -0,0 +1,37 @@
|
||||
-- Run this in Supabase SQL Editor if you get 403 "new row violates row-level security policy"
|
||||
-- when uploading an avatar. Fixes INSERT/UPDATE/DELETE policies to use JWT sub for folder match.
|
||||
|
||||
DROP POLICY IF EXISTS "Users can upload own avatar" ON storage.objects;
|
||||
DROP POLICY IF EXISTS "Users can update own avatar" ON storage.objects;
|
||||
DROP POLICY IF EXISTS "Users can delete own avatar" ON storage.objects;
|
||||
DROP POLICY IF EXISTS "Avatar images are publicly readable" ON storage.objects;
|
||||
|
||||
-- First folder in path must equal the authenticated user's id (from JWT sub)
|
||||
CREATE POLICY "Users can upload own avatar"
|
||||
ON storage.objects FOR INSERT TO authenticated
|
||||
WITH CHECK (
|
||||
bucket_id = 'avatars'
|
||||
AND (storage.foldername(name))[1] = (auth.jwt()->>'sub')
|
||||
);
|
||||
|
||||
CREATE POLICY "Users can update own avatar"
|
||||
ON storage.objects FOR UPDATE TO authenticated
|
||||
USING (
|
||||
bucket_id = 'avatars'
|
||||
AND (storage.foldername(name))[1] = (auth.jwt()->>'sub')
|
||||
)
|
||||
WITH CHECK (
|
||||
bucket_id = 'avatars'
|
||||
AND (storage.foldername(name))[1] = (auth.jwt()->>'sub')
|
||||
);
|
||||
|
||||
CREATE POLICY "Users can delete own avatar"
|
||||
ON storage.objects FOR DELETE TO authenticated
|
||||
USING (
|
||||
bucket_id = 'avatars'
|
||||
AND (storage.foldername(name))[1] = (auth.jwt()->>'sub')
|
||||
);
|
||||
|
||||
CREATE POLICY "Avatar images are publicly readable"
|
||||
ON storage.objects FOR SELECT TO public
|
||||
USING (bucket_id = 'avatars');
|
||||
@@ -0,0 +1,26 @@
|
||||
-- Run this in Supabase SQL Editor if you get "permission denied for table profiles"
|
||||
-- 1) Grants the Supabase API roles access to the omotomo schema and profiles table.
|
||||
-- 2) Ensures RLS policies exist on omotomo.profiles (skip policy creation if you already have them).
|
||||
|
||||
-- Allow anon and authenticated roles to use the omotomo schema
|
||||
GRANT USAGE ON SCHEMA omotomo TO anon, authenticated;
|
||||
|
||||
-- Allow reading and writing profiles (RLS policies control which rows)
|
||||
GRANT SELECT, INSERT, UPDATE, DELETE ON omotomo.profiles TO anon, authenticated;
|
||||
|
||||
-- RLS: ensure the table is protected and policies allow the right access
|
||||
ALTER TABLE omotomo.profiles ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
-- Drop existing policies if you need to recreate (optional; remove the DROP lines if policies already work)
|
||||
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;
|
||||
|
||||
-- Anyone can read profiles (e.g. show creator names on community decks)
|
||||
CREATE POLICY profiles_select ON omotomo.profiles FOR SELECT USING (true);
|
||||
|
||||
-- Users can insert/update/delete only their own profile (id = auth.uid())
|
||||
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);
|
||||
Reference in New Issue
Block a user