From e686095010d1753bb5262f9ef9bd26d4bde5ccd7 Mon Sep 17 00:00:00 2001 From: admin Date: Sat, 22 Aug 2026 23:10:57 +0000 Subject: [PATCH] Terminal --- .../20260807000300_student_alerts.sql | 178 ------------------ 1 file changed, 178 deletions(-) delete mode 100644 supabase/migrations/20260807000300_student_alerts.sql diff --git a/supabase/migrations/20260807000300_student_alerts.sql b/supabase/migrations/20260807000300_student_alerts.sql deleted file mode 100644 index cadd70d..0000000 --- a/supabase/migrations/20260807000300_student_alerts.sql +++ /dev/null @@ -1,178 +0,0 @@ --- Critical student alerts — spec section 6. --- --- These are the red banners: custody limits, unauthorized pickups, severe --- allergies, court-ordered communication restrictions. The spec requires that --- every view and every acknowledgment be auditable, so both are recorded as --- rows rather than as a boolean on the alert. --- --- Visibility is an explicit role array rather than a single sensitivity level. --- A severe-allergy alert must reach teachers and front-desk staff; a --- court-order alert usually must not. One column expresses both. - -CREATE TABLE IF NOT EXISTS public.student_alerts ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - student_id UUID NOT NULL REFERENCES public.students(id) ON DELETE CASCADE, - - alert_type TEXT NOT NULL, - severity TEXT NOT NULL DEFAULT 'critical', - title TEXT NOT NULL, - description TEXT NOT NULL, - - effective_date DATE NOT NULL DEFAULT CURRENT_DATE, - expiration_date DATE, - - document_path TEXT, - parenting_plan_id UUID REFERENCES public.parenting_plans(id) ON DELETE SET NULL, - - -- Which staff roles see this alert. Org admins always see everything. - visible_to_roles app_role[] NOT NULL - DEFAULT ARRAY['admin','org_admin','super_admin','campus_admin','management','teacher','staff']::app_role[], - - requires_acknowledgment BOOLEAN NOT NULL DEFAULT TRUE, - is_active BOOLEAN NOT NULL DEFAULT TRUE, - - created_by UUID REFERENCES auth.users(id), - created_at TIMESTAMPTZ NOT NULL DEFAULT now(), - updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), - - CONSTRAINT sa_type_valid CHECK (alert_type IN ( - 'parenting_plan','custody','unauthorized_pickup','medical','allergy', - 'court_order','safety','dismissal','other')), - CONSTRAINT sa_severity_valid CHECK (severity IN ('critical','high','info')), - CONSTRAINT sa_dates_ordered - CHECK (expiration_date IS NULL OR effective_date <= expiration_date) -); - -GRANT SELECT, INSERT, UPDATE, DELETE ON public.student_alerts TO authenticated; -GRANT ALL ON public.student_alerts TO service_role; -ALTER TABLE public.student_alerts ENABLE ROW LEVEL SECURITY; - -CREATE INDEX IF NOT EXISTS sa_student_idx ON public.student_alerts (student_id); --- The hot path: "show me this student's live alerts". Partial, because expired --- and deactivated alerts are never on that screen. -CREATE INDEX IF NOT EXISTS sa_active_idx ON public.student_alerts (student_id, severity) - WHERE is_active; -CREATE INDEX IF NOT EXISTS sa_plan_idx ON public.student_alerts (parenting_plan_id); - -DROP TRIGGER IF EXISTS trg_sa_upd ON public.student_alerts; -CREATE TRIGGER trg_sa_upd BEFORE UPDATE ON public.student_alerts - FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); - --- ============================================================================ --- ACKNOWLEDGMENTS --- ============================================================================ --- One row per staff member per alert. UNIQUE keeps it idempotent so a --- double-click cannot fabricate two acknowledgment records. - -CREATE TABLE IF NOT EXISTS public.student_alert_acknowledgments ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - alert_id UUID NOT NULL REFERENCES public.student_alerts(id) ON DELETE CASCADE, - user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE, - acknowledged_at TIMESTAMPTZ NOT NULL DEFAULT now(), - note TEXT, - UNIQUE (alert_id, user_id) -); - -GRANT SELECT, INSERT ON public.student_alert_acknowledgments TO authenticated; -GRANT ALL ON public.student_alert_acknowledgments TO service_role; -ALTER TABLE public.student_alert_acknowledgments ENABLE ROW LEVEL SECURITY; -CREATE INDEX IF NOT EXISTS saa_alert_idx ON public.student_alert_acknowledgments (alert_id); -CREATE INDEX IF NOT EXISTS saa_user_idx ON public.student_alert_acknowledgments (user_id); - --- ============================================================================ --- VIEW LOG --- ============================================================================ --- Append-only. No UNIQUE constraint: the point is to record every occasion the --- alert was displayed, not merely that it was seen once. Deliberately has no --- UPDATE or DELETE grant for authenticated — it is evidence. - -CREATE TABLE IF NOT EXISTS public.student_alert_views ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - alert_id UUID NOT NULL REFERENCES public.student_alerts(id) ON DELETE CASCADE, - user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE, - viewed_at TIMESTAMPTZ NOT NULL DEFAULT now() -); - -GRANT SELECT, INSERT ON public.student_alert_views TO authenticated; -GRANT ALL ON public.student_alert_views TO service_role; -ALTER TABLE public.student_alert_views ENABLE ROW LEVEL SECURITY; -CREATE INDEX IF NOT EXISTS sav_alert_idx ON public.student_alert_views (alert_id, viewed_at DESC); -CREATE INDEX IF NOT EXISTS sav_user_idx ON public.student_alert_views (user_id); - --- ============================================================================ --- HELPERS --- ============================================================================ - --- Currently-in-force alerts for a student that the caller is cleared to see. -CREATE OR REPLACE FUNCTION public.active_student_alerts(_student UUID) -RETURNS SETOF public.student_alerts -LANGUAGE SQL STABLE SECURITY DEFINER SET search_path = public -AS $$ - SELECT a.* FROM public.student_alerts a - WHERE a.student_id = _student - AND a.is_active - AND a.effective_date <= CURRENT_DATE - AND (a.expiration_date IS NULL OR a.expiration_date >= CURRENT_DATE) - AND (public.is_org_admin() OR public.current_user_has_any_role(a.visible_to_roles)) - ORDER BY CASE a.severity WHEN 'critical' THEN 0 WHEN 'high' THEN 1 ELSE 2 END, - a.effective_date DESC -$$; - --- Alerts still awaiting this user's acknowledgment. -CREATE OR REPLACE FUNCTION public.unacknowledged_alerts(_student UUID) -RETURNS SETOF public.student_alerts -LANGUAGE SQL STABLE SECURITY DEFINER SET search_path = public -AS $$ - SELECT a.* FROM public.active_student_alerts(_student) a - WHERE a.requires_acknowledgment - AND NOT EXISTS ( - SELECT 1 FROM public.student_alert_acknowledgments k - WHERE k.alert_id = a.id AND k.user_id = (SELECT auth.uid()) - ) -$$; - --- ============================================================================ --- POLICIES --- ============================================================================ - --- Staff see alerts targeted at their role; parents see their own child's --- alerts only when the alert is not restricted away from them. -DROP POLICY IF EXISTS "alerts read" ON public.student_alerts; -CREATE POLICY "alerts read" ON public.student_alerts FOR SELECT TO authenticated - USING ( - public.is_org_admin() - OR public.is_auditor() - OR (public.can_access_student(student_id) - AND public.current_user_has_any_role(visible_to_roles)) - OR (public.is_parent_of(student_id) AND 'parent'::app_role = ANY(visible_to_roles)) - ); - -DROP POLICY IF EXISTS "alerts manage" ON public.student_alerts; -CREATE POLICY "alerts manage" ON public.student_alerts FOR ALL TO authenticated - USING (public.can_manage_student(student_id)) - WITH CHECK (public.can_manage_student(student_id)); - --- A user may only record their own acknowledgment, and never edit it after. -DROP POLICY IF EXISTS "alert ack read" ON public.student_alert_acknowledgments; -CREATE POLICY "alert ack read" ON public.student_alert_acknowledgments FOR SELECT TO authenticated - USING ( - public.is_org_admin() OR public.is_auditor() OR user_id = (SELECT auth.uid()) - OR EXISTS (SELECT 1 FROM public.student_alerts a - WHERE a.id = alert_id AND public.can_manage_student(a.student_id)) - ); - -DROP POLICY IF EXISTS "alert ack self insert" ON public.student_alert_acknowledgments; -CREATE POLICY "alert ack self insert" ON public.student_alert_acknowledgments FOR INSERT TO authenticated - WITH CHECK (user_id = (SELECT auth.uid())); - -DROP POLICY IF EXISTS "alert views read" ON public.student_alert_views; -CREATE POLICY "alert views read" ON public.student_alert_views FOR SELECT TO authenticated - USING ( - public.is_org_admin() OR public.is_auditor() OR user_id = (SELECT auth.uid()) - OR EXISTS (SELECT 1 FROM public.student_alerts a - WHERE a.id = alert_id AND public.can_manage_student(a.student_id)) - ); - -DROP POLICY IF EXISTS "alert views self insert" ON public.student_alert_views; -CREATE POLICY "alert views self insert" ON public.student_alert_views FOR INSERT TO authenticated - WITH CHECK (user_id = (SELECT auth.uid()));