-- Migración 012: Configuración de destinatarios de notificaciones -- -- Mueve "quién recibe cada correo" desde variables de entorno (Dokploy) a la BD, -- gestionable desde la app. Dos tablas: -- notification_groups — grupos de destinatarios con un nivel de info fijo -- notification_scenario_recipients — qué grupos recibe cada escenario (event_type) -- -- IMPORTANTE: schema natur_reservas (la instancia Supabase es compartida; public es -- otro proyecto). Ver CLAUDE.md §1. -- ──────────────────────────────────────────────────────────── -- Grupos de destinatarios -- ──────────────────────────────────────────────────────────── CREATE TABLE natur_reservas.notification_groups ( id UUID DEFAULT gen_random_uuid() PRIMARY KEY, name TEXT NOT NULL, level TEXT NOT NULL DEFAULT 'completa' CHECK (level IN ('reducida', 'completa')), emails TEXT[] NOT NULL DEFAULT '{}', enabled BOOLEAN NOT NULL DEFAULT true, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now() ); COMMENT ON TABLE natur_reservas.notification_groups IS 'Grupos de destinatarios de notificaciones. level=reducida solo puede recibir la versión reducida de los correos (regla de privacidad).'; -- ──────────────────────────────────────────────────────────── -- Asignación escenario → grupos (N:M) -- ──────────────────────────────────────────────────────────── CREATE TABLE natur_reservas.notification_scenario_recipients ( id UUID DEFAULT gen_random_uuid() PRIMARY KEY, scenario TEXT NOT NULL, -- igual que notification_events.event_type group_id UUID NOT NULL REFERENCES natur_reservas.notification_groups(id) ON DELETE CASCADE, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), CONSTRAINT uq_scenario_group UNIQUE (scenario, group_id) ); CREATE INDEX idx_scenario_recipients_scenario ON natur_reservas.notification_scenario_recipients(scenario); COMMENT ON TABLE natur_reservas.notification_scenario_recipients IS 'Qué grupos reciben cada escenario (event_type). El motor de envío en apps/api lo consulta con service_role.'; -- ──────────────────────────────────────────────────────────── -- RLS: el acceso del frontend va por el API (service_role bypasea RLS). -- Bloqueamos acceso directo de anon/authenticated; el API valida admin. -- ──────────────────────────────────────────────────────────── ALTER TABLE natur_reservas.notification_groups ENABLE ROW LEVEL SECURITY; ALTER TABLE natur_reservas.notification_scenario_recipients ENABLE ROW LEVEL SECURITY; -- El API accede con service_role: bypasea RLS, pero necesita el GRANT de tabla -- (en schemas custom no se concede automáticamente, a diferencia de public). GRANT USAGE ON SCHEMA natur_reservas TO service_role; GRANT ALL ON natur_reservas.notification_groups TO service_role; GRANT ALL ON natur_reservas.notification_scenario_recipients TO service_role; REVOKE ALL ON natur_reservas.notification_groups FROM anon; REVOKE ALL ON natur_reservas.notification_scenario_recipients FROM anon; -- ──────────────────────────────────────────────────────────── -- Semilla inicial (editable después desde la pantalla) -- ──────────────────────────────────────────────────────────── WITH nuevos AS ( INSERT INTO natur_reservas.notification_groups (name, level, emails) VALUES ('Teneriffa', 'reducida', ARRAY['uwe.dotschkis@teneriffa2000.de', 'j.reichstein@teneriffa2000.de']), ('Naturcalabacera (Admin)', 'completa', ARRAY['administracion@naturcalabacera.com']), ('Limpieza', 'completa', ARRAY['limpieza.naturcalabacera@gmail.com']) -- placeholder editable RETURNING id, name ) INSERT INTO natur_reservas.notification_scenario_recipients (scenario, group_id) SELECT s.scenario, n.id FROM nuevos n JOIN (VALUES ('Teneriffa', 'reservation.created'), ('Teneriffa', 'reservation.updated'), ('Teneriffa', 'reservation.cancelled'), ('Naturcalabacera (Admin)', 'reservation.created'), ('Naturcalabacera (Admin)', 'reservation.updated'), ('Naturcalabacera (Admin)', 'reservation.cancelled'), ('Naturcalabacera (Admin)', 'reservation.invoice_second_notice'), ('Limpieza', 'reservation.pool_heating_notice') ) AS s(group_name, scenario) ON s.group_name = n.name;