From 501026093ccf6b0544c11010f82f35c2d9d14dc2 Mon Sep 17 00:00:00 2001 From: admin Date: Fri, 7 Aug 2026 04:12:01 +0000 Subject: [PATCH] Modified by www.SourceFiles.app --- ...4_8e1363a9-61d3-41e5-a97b-714ba925a202.sql | 402 ------------------ 1 file changed, 402 deletions(-) delete mode 100644 supabase/migrations/20260630204014_8e1363a9-61d3-41e5-a97b-714ba925a202.sql diff --git a/supabase/migrations/20260630204014_8e1363a9-61d3-41e5-a97b-714ba925a202.sql b/supabase/migrations/20260630204014_8e1363a9-61d3-41e5-a97b-714ba925a202.sql deleted file mode 100644 index b922390..0000000 --- a/supabase/migrations/20260630204014_8e1363a9-61d3-41e5-a97b-714ba925a202.sql +++ /dev/null @@ -1,402 +0,0 @@ - --- ============ ENUMS ============ -CREATE TYPE public.app_role AS ENUM ('admin', 'teacher', 'parent'); -CREATE TYPE public.attendance_status AS ENUM ('present', 'absent', 'late', 'excused'); -CREATE TYPE public.ledger_kind AS ENUM ('charge', 'payment'); -CREATE TYPE public.ledger_category AS ENUM ('tuition', 'late_pickup', 'activity', 'other'); - --- ============ PROFILES ============ -CREATE TABLE public.profiles ( - id UUID PRIMARY KEY REFERENCES auth.users(id) ON DELETE CASCADE, - full_name TEXT, - email TEXT, - phone TEXT, - created_at TIMESTAMPTZ NOT NULL DEFAULT now(), - updated_at TIMESTAMPTZ NOT NULL DEFAULT now() -); -GRANT SELECT, INSERT, UPDATE ON public.profiles TO authenticated; -GRANT ALL ON public.profiles TO service_role; -ALTER TABLE public.profiles ENABLE ROW LEVEL SECURITY; - --- ============ USER ROLES ============ -CREATE TABLE public.user_roles ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE, - role app_role NOT NULL, - created_at TIMESTAMPTZ NOT NULL DEFAULT now(), - UNIQUE (user_id, role) -); -GRANT SELECT ON public.user_roles TO authenticated; -GRANT ALL ON public.user_roles TO service_role; -ALTER TABLE public.user_roles ENABLE ROW LEVEL SECURITY; - -CREATE OR REPLACE FUNCTION public.has_role(_user_id UUID, _role app_role) -RETURNS BOOLEAN -LANGUAGE SQL STABLE SECURITY DEFINER SET search_path = public -AS $$ - SELECT EXISTS (SELECT 1 FROM public.user_roles WHERE user_id = _user_id AND role = _role) -$$; - -CREATE OR REPLACE FUNCTION public.current_user_has_role(_role app_role) -RETURNS BOOLEAN -LANGUAGE SQL STABLE SECURITY DEFINER SET search_path = public -AS $$ - SELECT EXISTS (SELECT 1 FROM public.user_roles WHERE user_id = auth.uid() AND role = _role) -$$; - --- ============ CLASSES ============ -CREATE TABLE public.classes ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - name TEXT NOT NULL, - teacher_id UUID REFERENCES auth.users(id) ON DELETE SET NULL, - created_at TIMESTAMPTZ NOT NULL DEFAULT now() -); -GRANT SELECT, INSERT, UPDATE, DELETE ON public.classes TO authenticated; -GRANT ALL ON public.classes TO service_role; -ALTER TABLE public.classes ENABLE ROW LEVEL SECURITY; - --- ============ STUDENTS ============ -CREATE TABLE public.students ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - first_name TEXT NOT NULL, - last_name TEXT NOT NULL, - dob DATE, - class_id UUID REFERENCES public.classes(id) ON DELETE SET NULL, - allergies TEXT, - photo_release BOOLEAN NOT NULL DEFAULT FALSE, - notes TEXT, - created_at TIMESTAMPTZ NOT NULL DEFAULT now(), - updated_at TIMESTAMPTZ NOT NULL DEFAULT now() -); -GRANT SELECT, INSERT, UPDATE, DELETE ON public.students TO authenticated; -GRANT ALL ON public.students TO service_role; -ALTER TABLE public.students ENABLE ROW LEVEL SECURITY; - --- ============ PARENT-STUDENT LINKS ============ -CREATE TABLE public.parent_students ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - parent_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE, - student_id UUID NOT NULL REFERENCES public.students(id) ON DELETE CASCADE, - relationship TEXT, - created_at TIMESTAMPTZ NOT NULL DEFAULT now(), - UNIQUE (parent_id, student_id) -); -GRANT SELECT, INSERT, UPDATE, DELETE ON public.parent_students TO authenticated; -GRANT ALL ON public.parent_students TO service_role; -ALTER TABLE public.parent_students ENABLE ROW LEVEL SECURITY; - -CREATE OR REPLACE FUNCTION public.is_parent_of(_student UUID) -RETURNS BOOLEAN -LANGUAGE SQL STABLE SECURITY DEFINER SET search_path = public -AS $$ - SELECT EXISTS (SELECT 1 FROM public.parent_students WHERE parent_id = auth.uid() AND student_id = _student) -$$; - -CREATE OR REPLACE FUNCTION public.teaches_student(_student UUID) -RETURNS BOOLEAN -LANGUAGE SQL STABLE SECURITY DEFINER SET search_path = public -AS $$ - SELECT EXISTS ( - SELECT 1 FROM public.students s - JOIN public.classes c ON c.id = s.class_id - WHERE s.id = _student AND c.teacher_id = auth.uid() - ) -$$; - --- ============ AUTHORIZED PICKUPS ============ -CREATE TABLE public.authorized_pickups ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - student_id UUID NOT NULL REFERENCES public.students(id) ON DELETE CASCADE, - name TEXT NOT NULL, - phone TEXT, - relationship TEXT, - created_at TIMESTAMPTZ NOT NULL DEFAULT now() -); -GRANT SELECT, INSERT, UPDATE, DELETE ON public.authorized_pickups TO authenticated; -GRANT ALL ON public.authorized_pickups TO service_role; -ALTER TABLE public.authorized_pickups ENABLE ROW LEVEL SECURITY; - --- ============ CONTRACTS (admin only) ============ -CREATE TABLE public.contracts ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - student_id UUID NOT NULL REFERENCES public.students(id) ON DELETE CASCADE, - file_path TEXT NOT NULL, - title TEXT, - uploaded_by UUID REFERENCES auth.users(id), - created_at TIMESTAMPTZ NOT NULL DEFAULT now() -); -GRANT SELECT, INSERT, UPDATE, DELETE ON public.contracts TO authenticated; -GRANT ALL ON public.contracts TO service_role; -ALTER TABLE public.contracts ENABLE ROW LEVEL SECURITY; - --- ============ ATTENDANCE ============ -CREATE TABLE public.attendance ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - student_id UUID NOT NULL REFERENCES public.students(id) ON DELETE CASCADE, - date DATE NOT NULL, - status attendance_status NOT NULL DEFAULT 'present', - recorded_by UUID REFERENCES auth.users(id), - note TEXT, - created_at TIMESTAMPTZ NOT NULL DEFAULT now(), - UNIQUE (student_id, date) -); -GRANT SELECT, INSERT, UPDATE, DELETE ON public.attendance TO authenticated; -GRANT ALL ON public.attendance TO service_role; -ALTER TABLE public.attendance ENABLE ROW LEVEL SECURITY; - --- ============ LEDGER ============ -CREATE TABLE public.ledger_entries ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - student_id UUID NOT NULL REFERENCES public.students(id) ON DELETE CASCADE, - date DATE NOT NULL DEFAULT CURRENT_DATE, - kind ledger_kind NOT NULL, - category ledger_category NOT NULL DEFAULT 'tuition', - amount_cents INTEGER NOT NULL, - note TEXT, - created_by UUID REFERENCES auth.users(id), - created_at TIMESTAMPTZ NOT NULL DEFAULT now() -); -GRANT SELECT, INSERT, UPDATE, DELETE ON public.ledger_entries TO authenticated; -GRANT ALL ON public.ledger_entries TO service_role; -ALTER TABLE public.ledger_entries ENABLE ROW LEVEL SECURITY; - --- ============ MESSAGING ============ -CREATE TABLE public.message_threads ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - subject TEXT, - student_id UUID REFERENCES public.students(id) ON DELETE SET NULL, - created_by UUID NOT NULL REFERENCES auth.users(id), - created_at TIMESTAMPTZ NOT NULL DEFAULT now(), - updated_at TIMESTAMPTZ NOT NULL DEFAULT now() -); -GRANT SELECT, INSERT, UPDATE, DELETE ON public.message_threads TO authenticated; -GRANT ALL ON public.message_threads TO service_role; -ALTER TABLE public.message_threads ENABLE ROW LEVEL SECURITY; - -CREATE TABLE public.thread_participants ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - thread_id UUID NOT NULL REFERENCES public.message_threads(id) ON DELETE CASCADE, - user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE, - UNIQUE (thread_id, user_id) -); -GRANT SELECT, INSERT, UPDATE, DELETE ON public.thread_participants TO authenticated; -GRANT ALL ON public.thread_participants TO service_role; -ALTER TABLE public.thread_participants ENABLE ROW LEVEL SECURITY; - -CREATE OR REPLACE FUNCTION public.is_thread_participant(_thread UUID) -RETURNS BOOLEAN -LANGUAGE SQL STABLE SECURITY DEFINER SET search_path = public -AS $$ - SELECT EXISTS (SELECT 1 FROM public.thread_participants WHERE thread_id = _thread AND user_id = auth.uid()) -$$; - -CREATE TABLE public.messages ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - thread_id UUID NOT NULL REFERENCES public.message_threads(id) ON DELETE CASCADE, - sender_id UUID NOT NULL REFERENCES auth.users(id), - body TEXT NOT NULL, - created_at TIMESTAMPTZ NOT NULL DEFAULT now() -); -GRANT SELECT, INSERT ON public.messages TO authenticated; -GRANT ALL ON public.messages TO service_role; -ALTER TABLE public.messages ENABLE ROW LEVEL SECURITY; - --- ============ FORMS ============ -CREATE TABLE public.forms ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - title TEXT NOT NULL, - description TEXT, - schema_json JSONB NOT NULL DEFAULT '[]'::jsonb, - active BOOLEAN NOT NULL DEFAULT TRUE, - created_by UUID REFERENCES auth.users(id), - created_at TIMESTAMPTZ NOT NULL DEFAULT now() -); -GRANT SELECT, INSERT, UPDATE, DELETE ON public.forms TO authenticated; -GRANT ALL ON public.forms TO service_role; -ALTER TABLE public.forms ENABLE ROW LEVEL SECURITY; - -CREATE TABLE public.form_responses ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - form_id UUID NOT NULL REFERENCES public.forms(id) ON DELETE CASCADE, - student_id UUID REFERENCES public.students(id) ON DELETE SET NULL, - submitted_by UUID NOT NULL REFERENCES auth.users(id), - data_json JSONB NOT NULL DEFAULT '{}'::jsonb, - submitted_at TIMESTAMPTZ NOT NULL DEFAULT now() -); -GRANT SELECT, INSERT ON public.form_responses TO authenticated; -GRANT ALL ON public.form_responses TO service_role; -ALTER TABLE public.form_responses ENABLE ROW LEVEL SECURITY; - --- ============ CALENDAR ============ -CREATE TABLE public.calendar_events ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - date DATE NOT NULL, - end_date DATE, - title TEXT NOT NULL, - description TEXT, - created_by UUID REFERENCES auth.users(id), - created_at TIMESTAMPTZ NOT NULL DEFAULT now() -); -GRANT SELECT, INSERT, UPDATE, DELETE ON public.calendar_events TO authenticated; -GRANT ALL ON public.calendar_events TO service_role; -ALTER TABLE public.calendar_events ENABLE ROW LEVEL SECURITY; - --- ============ POLICIES ============ - --- profiles: each user sees their own; admins see all -CREATE POLICY "own profile read" ON public.profiles FOR SELECT TO authenticated - USING (id = auth.uid() OR public.current_user_has_role('admin')); -CREATE POLICY "own profile update" ON public.profiles FOR UPDATE TO authenticated - USING (id = auth.uid() OR public.current_user_has_role('admin')); -CREATE POLICY "own profile insert" ON public.profiles FOR INSERT TO authenticated - WITH CHECK (id = auth.uid()); - --- user_roles: users see their own, admins manage all -CREATE POLICY "see own roles" ON public.user_roles FOR SELECT TO authenticated - USING (user_id = auth.uid() OR public.current_user_has_role('admin')); -CREATE POLICY "admins manage roles" ON public.user_roles FOR ALL TO authenticated - USING (public.current_user_has_role('admin')) WITH CHECK (public.current_user_has_role('admin')); - --- classes -CREATE POLICY "classes read" ON public.classes FOR SELECT TO authenticated - USING (public.current_user_has_role('admin') OR teacher_id = auth.uid() OR public.current_user_has_role('parent')); -CREATE POLICY "admins manage classes" ON public.classes FOR ALL TO authenticated - USING (public.current_user_has_role('admin')) WITH CHECK (public.current_user_has_role('admin')); - --- students -CREATE POLICY "students read" ON public.students FOR SELECT TO authenticated - USING ( - public.current_user_has_role('admin') - OR public.is_parent_of(id) - OR (class_id IS NOT NULL AND EXISTS (SELECT 1 FROM public.classes c WHERE c.id = class_id AND c.teacher_id = auth.uid())) - ); -CREATE POLICY "admins manage students" ON public.students FOR ALL TO authenticated - USING (public.current_user_has_role('admin')) WITH CHECK (public.current_user_has_role('admin')); - --- parent_students -CREATE POLICY "parent_students read" ON public.parent_students FOR SELECT TO authenticated - USING ( - public.current_user_has_role('admin') - OR parent_id = auth.uid() - OR public.teaches_student(student_id) - ); -CREATE POLICY "admins manage parent_students" ON public.parent_students FOR ALL TO authenticated - USING (public.current_user_has_role('admin')) WITH CHECK (public.current_user_has_role('admin')); - --- authorized_pickups -CREATE POLICY "pickups read" ON public.authorized_pickups FOR SELECT TO authenticated - USING ( - public.current_user_has_role('admin') - OR public.is_parent_of(student_id) - OR public.teaches_student(student_id) - ); -CREATE POLICY "pickups write parent or admin" ON public.authorized_pickups FOR ALL TO authenticated - USING (public.current_user_has_role('admin') OR public.is_parent_of(student_id)) - WITH CHECK (public.current_user_has_role('admin') OR public.is_parent_of(student_id)); - --- contracts: ADMIN ONLY -CREATE POLICY "contracts admin only" ON public.contracts FOR ALL TO authenticated - USING (public.current_user_has_role('admin')) WITH CHECK (public.current_user_has_role('admin')); - --- attendance -CREATE POLICY "attendance read" ON public.attendance FOR SELECT TO authenticated - USING ( - public.current_user_has_role('admin') - OR public.is_parent_of(student_id) - OR public.teaches_student(student_id) - ); -CREATE POLICY "attendance write teacher or admin" ON public.attendance FOR ALL TO authenticated - USING (public.current_user_has_role('admin') OR public.teaches_student(student_id)) - WITH CHECK (public.current_user_has_role('admin') OR public.teaches_student(student_id)); - --- ledger -CREATE POLICY "ledger read" ON public.ledger_entries FOR SELECT TO authenticated - USING (public.current_user_has_role('admin') OR public.is_parent_of(student_id)); -CREATE POLICY "ledger admin manage" ON public.ledger_entries FOR ALL TO authenticated - USING (public.current_user_has_role('admin')) WITH CHECK (public.current_user_has_role('admin')); - --- threads -CREATE POLICY "threads participants read" ON public.message_threads FOR SELECT TO authenticated - USING (public.current_user_has_role('admin') OR public.is_thread_participant(id)); -CREATE POLICY "threads create" ON public.message_threads FOR INSERT TO authenticated - WITH CHECK (created_by = auth.uid()); -CREATE POLICY "threads admin manage" ON public.message_threads FOR UPDATE TO authenticated - USING (public.current_user_has_role('admin')); - -CREATE POLICY "tp read" ON public.thread_participants FOR SELECT TO authenticated - USING (public.current_user_has_role('admin') OR user_id = auth.uid() OR public.is_thread_participant(thread_id)); -CREATE POLICY "tp insert" ON public.thread_participants FOR INSERT TO authenticated - WITH CHECK ( - public.current_user_has_role('admin') - OR EXISTS (SELECT 1 FROM public.message_threads t WHERE t.id = thread_id AND t.created_by = auth.uid()) - ); - -CREATE POLICY "msg read" ON public.messages FOR SELECT TO authenticated - USING (public.current_user_has_role('admin') OR public.is_thread_participant(thread_id)); -CREATE POLICY "msg send" ON public.messages FOR INSERT TO authenticated - WITH CHECK (sender_id = auth.uid() AND (public.current_user_has_role('admin') OR public.is_thread_participant(thread_id))); - --- forms -CREATE POLICY "forms read all" ON public.forms FOR SELECT TO authenticated USING (TRUE); -CREATE POLICY "forms admin manage" ON public.forms FOR ALL TO authenticated - USING (public.current_user_has_role('admin')) WITH CHECK (public.current_user_has_role('admin')); - -CREATE POLICY "form_responses read" ON public.form_responses FOR SELECT TO authenticated - USING (public.current_user_has_role('admin') OR submitted_by = auth.uid()); -CREATE POLICY "form_responses submit" ON public.form_responses FOR INSERT TO authenticated - WITH CHECK (submitted_by = auth.uid()); - --- calendar -CREATE POLICY "calendar read" ON public.calendar_events FOR SELECT TO authenticated USING (TRUE); -CREATE POLICY "calendar admin manage" ON public.calendar_events FOR ALL TO authenticated - USING (public.current_user_has_role('admin')) WITH CHECK (public.current_user_has_role('admin')); - --- ============ STORAGE POLICIES (contracts bucket: admin only) ============ -CREATE POLICY "contracts bucket admin read" ON storage.objects FOR SELECT TO authenticated - USING (bucket_id = 'contracts' AND public.current_user_has_role('admin')); -CREATE POLICY "contracts bucket admin write" ON storage.objects FOR INSERT TO authenticated - WITH CHECK (bucket_id = 'contracts' AND public.current_user_has_role('admin')); -CREATE POLICY "contracts bucket admin update" ON storage.objects FOR UPDATE TO authenticated - USING (bucket_id = 'contracts' AND public.current_user_has_role('admin')); -CREATE POLICY "contracts bucket admin delete" ON storage.objects FOR DELETE TO authenticated - USING (bucket_id = 'contracts' AND public.current_user_has_role('admin')); - --- ============ AUTO PROFILE + FIRST-USER-ADMIN TRIGGER ============ -CREATE OR REPLACE FUNCTION public.handle_new_user() -RETURNS TRIGGER -LANGUAGE plpgsql -SECURITY DEFINER -SET search_path = public -AS $$ -DECLARE - user_count INTEGER; -BEGIN - INSERT INTO public.profiles (id, full_name, email) - VALUES (NEW.id, COALESCE(NEW.raw_user_meta_data->>'full_name', NEW.email), NEW.email) - ON CONFLICT (id) DO NOTHING; - - SELECT COUNT(*) INTO user_count FROM public.user_roles; - IF user_count = 0 THEN - INSERT INTO public.user_roles (user_id, role) VALUES (NEW.id, 'admin'); - ELSE - INSERT INTO public.user_roles (user_id, role) VALUES (NEW.id, 'parent') - ON CONFLICT (user_id, role) DO NOTHING; - END IF; - - RETURN NEW; -END; -$$; - -CREATE TRIGGER on_auth_user_created -AFTER INSERT ON auth.users -FOR EACH ROW EXECUTE FUNCTION public.handle_new_user(); - --- updated_at trigger function -CREATE OR REPLACE FUNCTION public.set_updated_at() -RETURNS TRIGGER LANGUAGE plpgsql AS $$ -BEGIN NEW.updated_at = now(); RETURN NEW; END; -$$; - -CREATE TRIGGER trg_profiles_upd BEFORE UPDATE ON public.profiles FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); -CREATE TRIGGER trg_students_upd BEFORE UPDATE ON public.students FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); -CREATE TRIGGER trg_threads_upd BEFORE UPDATE ON public.message_threads FOR EACH ROW EXECUTE FUNCTION public.set_updated_at();