-- supabase/migrations/20260712000016_in_app_notifications.sql -- Phase 5: in-app notification bell — read state, content columns, per-user RLS, -- and a SECURITY DEFINER writer so non-elevated reporters can trigger alerts. ALTER TABLE notifications_log ADD COLUMN recipient_user_id UUID REFERENCES users(id), ADD COLUMN read_at TIMESTAMPTZ, ADD COLUMN title TEXT, ADD COLUMN link TEXT; CREATE INDEX notifications_in_app_unread_idx ON notifications_log (recipient_user_id, sent_at DESC) WHERE channel = 'in_app' AND read_at IS NULL; -- Users see their own in-app notifications (elevated read policy already exists) CREATE POLICY "notifications_read_own" ON notifications_log FOR SELECT USING (recipient_user_id = auth.uid()); -- Users may only mark their own notifications read CREATE POLICY "notifications_update_own" ON notifications_log FOR UPDATE USING (recipient_user_id = auth.uid()) WITH CHECK (recipient_user_id = auth.uid()); -- Writer RPC: INSERT policy on notifications_log is elevated-roles-only, but a -- reporter submitting an incident must notify supervisors/HSE. Mirrors write_audit_log. CREATE OR REPLACE FUNCTION public.create_in_app_notification( p_recipient UUID, p_title TEXT, p_link TEXT DEFAULT NULL, p_incident_id UUID DEFAULT NULL, p_capa_id UUID DEFAULT NULL ) RETURNS void LANGUAGE plpgsql SECURITY DEFINER SET search_path = public AS $$ BEGIN IF auth.uid() IS NULL THEN RAISE EXCEPTION 'authentication required'; END IF; INSERT INTO public.notifications_log (channel, recipient, recipient_user_id, title, link, incident_id, capa_id) VALUES ('in_app', p_recipient::text, p_recipient, p_title, p_link, p_incident_id, p_capa_id); END; $$;