You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

118 lines
3.4 KiB

-- 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()
)
);

Powered by TurnKey Linux.