-- Decks table (metadata + config for Decky quiz/flashcard app) CREATE TABLE IF NOT EXISTS public.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, description text NOT NULL DEFAULT '', config jsonb NOT NULL DEFAULT '{ "requiredConsecutiveCorrect": 3, "defaultAttemptSize": 10, "priorityIncreaseOnIncorrect": 5, "priorityDecreaseOnCorrect": 2, "immediateFeedbackEnabled": true, "includeKnownInAttempts": false, "shuffleAnswerOrder": true, "excludeFlaggedQuestions": false, "timeLimitSeconds": null }'::jsonb, published boolean NOT NULL DEFAULT false, created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now() ); -- Questions table (deck content) CREATE TABLE IF NOT EXISTS public.questions ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), deck_id uuid NOT NULL REFERENCES public.decks(id) ON DELETE CASCADE, sort_order int NOT NULL DEFAULT 0, prompt text NOT NULL, explanation text, answers jsonb NOT NULL DEFAULT '[]'::jsonb, 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; -- updated_at trigger for decks CREATE OR REPLACE FUNCTION public.set_decks_updated_at() RETURNS TRIGGER AS $$ BEGIN NEW.updated_at = now(); RETURN NEW; END; $$ LANGUAGE plpgsql; DROP TRIGGER IF EXISTS decks_updated_at ON public.decks; CREATE TRIGGER decks_updated_at BEFORE UPDATE ON public.decks FOR EACH ROW EXECUTE PROCEDURE public.set_decks_updated_at(); -- RLS ALTER TABLE public.decks ENABLE ROW LEVEL SECURITY; ALTER TABLE public.questions ENABLE ROW LEVEL SECURITY; -- decks: SELECT if owner or published; mutate only if owner CREATE POLICY decks_select ON public.decks FOR SELECT USING (owner_id = auth.uid() OR published = true); CREATE POLICY decks_insert ON public.decks FOR INSERT WITH CHECK (owner_id = auth.uid()); CREATE POLICY decks_update ON public.decks FOR UPDATE USING (owner_id = auth.uid()) WITH CHECK (owner_id = auth.uid()); CREATE POLICY decks_delete ON public.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 FOR SELECT USING ( EXISTS ( SELECT 1 FROM public.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 FOR INSERT WITH CHECK ( EXISTS ( SELECT 1 FROM public.decks d WHERE d.id = questions.deck_id AND d.owner_id = auth.uid() ) ); CREATE POLICY questions_update ON public.questions FOR UPDATE USING ( EXISTS ( SELECT 1 FROM public.decks d WHERE d.id = questions.deck_id AND d.owner_id = auth.uid() ) ) WITH CHECK ( EXISTS ( SELECT 1 FROM public.decks d WHERE d.id = questions.deck_id AND d.owner_id = auth.uid() ) ); CREATE POLICY questions_delete ON public.questions FOR DELETE USING ( EXISTS ( SELECT 1 FROM public.decks d WHERE d.id = questions.deck_id AND d.owner_id = auth.uid() ) );