refactor: remove obsolete member management API module
This commit is contained in:
@@ -0,0 +1,209 @@
|
||||
-- =====================================================
|
||||
-- Notification Rules + Scheduled Jobs
|
||||
--
|
||||
-- Configurable notification triggers per account.
|
||||
-- Scheduled job runner with tracking.
|
||||
-- Pending notifications queue for async dispatch.
|
||||
-- =====================================================
|
||||
|
||||
-- 1. Notification rules — configurable triggers per account
|
||||
CREATE TABLE IF NOT EXISTS public.member_notification_rules (
|
||||
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
account_id uuid NOT NULL REFERENCES public.accounts(id) ON DELETE CASCADE,
|
||||
trigger_event text NOT NULL CHECK (trigger_event IN (
|
||||
'application.submitted', 'application.approved', 'application.rejected',
|
||||
'member.created', 'member.status_changed',
|
||||
'member.birthday', 'member.anniversary',
|
||||
'dues.unpaid', 'mandate.revoked'
|
||||
)),
|
||||
channel text NOT NULL DEFAULT 'in_app' CHECK (channel IN ('in_app', 'email', 'both')),
|
||||
recipient_type text NOT NULL CHECK (recipient_type IN (
|
||||
'admin', 'member', 'specific_user', 'role_holder'
|
||||
)),
|
||||
recipient_config jsonb NOT NULL DEFAULT '{}',
|
||||
subject_template text,
|
||||
message_template text NOT NULL,
|
||||
is_active boolean NOT NULL DEFAULT true,
|
||||
created_at timestamptz NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE INDEX ix_notification_rules_account
|
||||
ON public.member_notification_rules(account_id, trigger_event)
|
||||
WHERE is_active = true;
|
||||
|
||||
ALTER TABLE public.member_notification_rules ENABLE ROW LEVEL SECURITY;
|
||||
REVOKE ALL ON public.member_notification_rules FROM authenticated, service_role;
|
||||
GRANT SELECT, INSERT, UPDATE, DELETE ON public.member_notification_rules TO authenticated;
|
||||
GRANT ALL ON public.member_notification_rules TO service_role;
|
||||
|
||||
CREATE POLICY notification_rules_select
|
||||
ON public.member_notification_rules FOR SELECT TO authenticated
|
||||
USING (public.has_role_on_account(account_id));
|
||||
|
||||
CREATE POLICY notification_rules_mutate
|
||||
ON public.member_notification_rules FOR ALL TO authenticated
|
||||
USING (public.has_permission(auth.uid(), account_id, 'members.write'::public.app_permissions));
|
||||
|
||||
-- 2. Scheduled job configuration per account
|
||||
CREATE TABLE IF NOT EXISTS public.scheduled_job_configs (
|
||||
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
account_id uuid NOT NULL REFERENCES public.accounts(id) ON DELETE CASCADE,
|
||||
job_type text NOT NULL CHECK (job_type IN (
|
||||
'birthday_notification', 'anniversary_notification',
|
||||
'dues_reminder', 'data_quality_check', 'gdpr_retention_check'
|
||||
)),
|
||||
is_enabled boolean NOT NULL DEFAULT true,
|
||||
config jsonb NOT NULL DEFAULT '{}',
|
||||
last_run_at timestamptz,
|
||||
next_run_at timestamptz,
|
||||
created_at timestamptz NOT NULL DEFAULT now(),
|
||||
UNIQUE(account_id, job_type)
|
||||
);
|
||||
|
||||
ALTER TABLE public.scheduled_job_configs ENABLE ROW LEVEL SECURITY;
|
||||
REVOKE ALL ON public.scheduled_job_configs FROM authenticated, service_role;
|
||||
GRANT SELECT, INSERT, UPDATE ON public.scheduled_job_configs TO authenticated;
|
||||
GRANT ALL ON public.scheduled_job_configs TO service_role;
|
||||
|
||||
CREATE POLICY scheduled_jobs_select
|
||||
ON public.scheduled_job_configs FOR SELECT TO authenticated
|
||||
USING (public.has_role_on_account(account_id));
|
||||
|
||||
CREATE POLICY scheduled_jobs_mutate
|
||||
ON public.scheduled_job_configs FOR ALL TO authenticated
|
||||
USING (public.has_permission(auth.uid(), account_id, 'members.write'::public.app_permissions));
|
||||
|
||||
-- 3. Job run history
|
||||
CREATE TABLE IF NOT EXISTS public.scheduled_job_runs (
|
||||
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
job_config_id uuid NOT NULL REFERENCES public.scheduled_job_configs(id) ON DELETE CASCADE,
|
||||
status text NOT NULL DEFAULT 'running' CHECK (status IN ('running', 'completed', 'failed')),
|
||||
result jsonb,
|
||||
started_at timestamptz NOT NULL DEFAULT now(),
|
||||
completed_at timestamptz
|
||||
);
|
||||
|
||||
CREATE INDEX ix_job_runs_config
|
||||
ON public.scheduled_job_runs(job_config_id, started_at DESC);
|
||||
|
||||
ALTER TABLE public.scheduled_job_runs ENABLE ROW LEVEL SECURITY;
|
||||
REVOKE ALL ON public.scheduled_job_runs FROM authenticated, service_role;
|
||||
GRANT SELECT ON public.scheduled_job_runs TO authenticated;
|
||||
GRANT ALL ON public.scheduled_job_runs TO service_role;
|
||||
|
||||
CREATE POLICY job_runs_select
|
||||
ON public.scheduled_job_runs FOR SELECT TO authenticated
|
||||
USING (EXISTS (
|
||||
SELECT 1 FROM public.scheduled_job_configs jc
|
||||
WHERE jc.id = scheduled_job_runs.job_config_id
|
||||
AND public.has_role_on_account(jc.account_id)
|
||||
));
|
||||
|
||||
-- 4. Pending notifications queue (lightweight, processed by cron)
|
||||
CREATE TABLE IF NOT EXISTS public.pending_member_notifications (
|
||||
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
||||
account_id uuid NOT NULL,
|
||||
trigger_event text NOT NULL,
|
||||
member_id uuid,
|
||||
context jsonb NOT NULL DEFAULT '{}',
|
||||
created_at timestamptz NOT NULL DEFAULT now(),
|
||||
processed_at timestamptz
|
||||
);
|
||||
|
||||
CREATE INDEX ix_pending_notifications_unprocessed
|
||||
ON public.pending_member_notifications(created_at)
|
||||
WHERE processed_at IS NULL;
|
||||
|
||||
-- No RLS — only service_role accesses this table
|
||||
REVOKE ALL ON public.pending_member_notifications FROM authenticated;
|
||||
GRANT ALL ON public.pending_member_notifications TO service_role;
|
||||
|
||||
-- 5. Trigger: queue notifications when audit events fire
|
||||
CREATE OR REPLACE FUNCTION public.queue_notification_on_audit()
|
||||
RETURNS trigger
|
||||
LANGUAGE plpgsql
|
||||
SECURITY DEFINER
|
||||
SET search_path = ''
|
||||
AS $$
|
||||
DECLARE
|
||||
v_event text;
|
||||
BEGIN
|
||||
-- Map audit action to notification trigger event
|
||||
v_event := CASE NEW.action
|
||||
WHEN 'created' THEN 'member.created'
|
||||
WHEN 'status_changed' THEN 'member.status_changed'
|
||||
WHEN 'application_approved' THEN 'application.approved'
|
||||
WHEN 'application_rejected' THEN 'application.rejected'
|
||||
ELSE NULL
|
||||
END;
|
||||
|
||||
IF v_event IS NULL THEN
|
||||
RETURN NEW;
|
||||
END IF;
|
||||
|
||||
-- Only queue if there are active rules for this event
|
||||
IF EXISTS (
|
||||
SELECT 1 FROM public.member_notification_rules
|
||||
WHERE account_id = NEW.account_id
|
||||
AND trigger_event = v_event
|
||||
AND is_active = true
|
||||
) THEN
|
||||
INSERT INTO public.pending_member_notifications (account_id, trigger_event, member_id, context)
|
||||
VALUES (
|
||||
NEW.account_id,
|
||||
v_event,
|
||||
NEW.member_id,
|
||||
jsonb_build_object(
|
||||
'audit_action', NEW.action,
|
||||
'changes', NEW.changes,
|
||||
'metadata', NEW.metadata,
|
||||
'user_id', NEW.user_id
|
||||
)
|
||||
);
|
||||
END IF;
|
||||
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$;
|
||||
|
||||
CREATE TRIGGER trg_audit_queue_notifications
|
||||
AFTER INSERT ON public.member_audit_log
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION public.queue_notification_on_audit();
|
||||
|
||||
-- 6. Queue trigger for application submissions (from membership_applications, not audit log)
|
||||
CREATE OR REPLACE FUNCTION public.queue_notification_on_application()
|
||||
RETURNS trigger
|
||||
LANGUAGE plpgsql
|
||||
SECURITY DEFINER
|
||||
SET search_path = ''
|
||||
AS $$
|
||||
BEGIN
|
||||
IF NEW.status = 'submitted' AND (TG_OP = 'INSERT' OR OLD.status IS DISTINCT FROM NEW.status) THEN
|
||||
IF EXISTS (
|
||||
SELECT 1 FROM public.member_notification_rules
|
||||
WHERE account_id = NEW.account_id
|
||||
AND trigger_event = 'application.submitted'
|
||||
AND is_active = true
|
||||
) THEN
|
||||
INSERT INTO public.pending_member_notifications (account_id, trigger_event, context)
|
||||
VALUES (
|
||||
NEW.account_id,
|
||||
'application.submitted',
|
||||
jsonb_build_object(
|
||||
'application_id', NEW.id,
|
||||
'first_name', NEW.first_name,
|
||||
'last_name', NEW.last_name,
|
||||
'email', NEW.email
|
||||
)
|
||||
);
|
||||
END IF;
|
||||
END IF;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$;
|
||||
|
||||
CREATE TRIGGER trg_application_queue_notifications
|
||||
AFTER INSERT OR UPDATE OF status ON public.membership_applications
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION public.queue_notification_on_application();
|
||||
Reference in New Issue
Block a user