feat: add shared notification, communication, and export services for bookings, courses, and events; introduce btree_gist extension and new booking atomic function
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
-- =====================================================
|
||||
-- Module Notification Rules & Queue
|
||||
-- Shared notification infrastructure for courses, events, bookings.
|
||||
-- =====================================================
|
||||
|
||||
-- Notification rules: define what triggers notifications
|
||||
CREATE TABLE IF NOT EXISTS public.module_notification_rules (
|
||||
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
account_id uuid NOT NULL REFERENCES public.accounts(id) ON DELETE CASCADE,
|
||||
module text NOT NULL CHECK (module IN ('courses', 'events', 'bookings')),
|
||||
trigger_event text NOT NULL CHECK (trigger_event IN (
|
||||
'course.participant_enrolled', 'course.participant_waitlisted', 'course.participant_promoted',
|
||||
'course.participant_cancelled', 'course.status_changed', 'course.session_reminder',
|
||||
'event.registration_confirmed', 'event.registration_waitlisted', 'event.registration_promoted',
|
||||
'event.registration_cancelled', 'event.status_changed', 'event.reminder',
|
||||
'booking.confirmed', 'booking.check_in_reminder', 'booking.checked_in',
|
||||
'booking.checked_out', 'booking.cancelled'
|
||||
)),
|
||||
channel text NOT NULL DEFAULT 'in_app' CHECK (channel IN ('in_app', 'email', 'both')),
|
||||
recipient_type text NOT NULL DEFAULT 'admin' CHECK (recipient_type IN ('admin', 'participant', 'guest', 'instructor', 'specific_user')),
|
||||
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 IF NOT EXISTS ix_module_notification_rules_lookup
|
||||
ON public.module_notification_rules(account_id, module, trigger_event)
|
||||
WHERE is_active = true;
|
||||
|
||||
ALTER TABLE public.module_notification_rules ENABLE ROW LEVEL SECURITY;
|
||||
REVOKE ALL ON public.module_notification_rules FROM authenticated, service_role;
|
||||
GRANT SELECT, INSERT, UPDATE, DELETE ON public.module_notification_rules TO authenticated;
|
||||
GRANT ALL ON public.module_notification_rules TO service_role;
|
||||
|
||||
CREATE POLICY module_notification_rules_select ON public.module_notification_rules
|
||||
FOR SELECT TO authenticated USING (public.has_role_on_account(account_id));
|
||||
|
||||
CREATE POLICY module_notification_rules_mutate ON public.module_notification_rules
|
||||
FOR ALL TO authenticated USING (
|
||||
public.has_permission(auth.uid(), account_id, 'settings.manage'::public.app_permissions)
|
||||
) WITH CHECK (
|
||||
public.has_permission(auth.uid(), account_id, 'settings.manage'::public.app_permissions)
|
||||
);
|
||||
|
||||
-- Pending notifications queue
|
||||
CREATE TABLE IF NOT EXISTS public.pending_module_notifications (
|
||||
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
||||
account_id uuid NOT NULL REFERENCES public.accounts(id) ON DELETE CASCADE,
|
||||
module text NOT NULL CHECK (module IN ('courses', 'events', 'bookings')),
|
||||
trigger_event text NOT NULL,
|
||||
entity_id uuid NOT NULL,
|
||||
context jsonb NOT NULL DEFAULT '{}',
|
||||
processed_at timestamptz,
|
||||
created_at timestamptz NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS ix_pending_module_notifications_unprocessed
|
||||
ON public.pending_module_notifications(created_at)
|
||||
WHERE processed_at IS NULL;
|
||||
|
||||
ALTER TABLE public.pending_module_notifications ENABLE ROW LEVEL SECURITY;
|
||||
REVOKE ALL ON public.pending_module_notifications FROM authenticated, service_role;
|
||||
GRANT SELECT ON public.pending_module_notifications TO authenticated;
|
||||
GRANT ALL ON public.pending_module_notifications TO service_role;
|
||||
|
||||
CREATE POLICY pending_module_notifications_select ON public.pending_module_notifications
|
||||
FOR SELECT TO authenticated USING (public.has_role_on_account(account_id));
|
||||
|
||||
-- Enqueue helper
|
||||
CREATE OR REPLACE FUNCTION public.enqueue_module_notification(
|
||||
p_account_id uuid,
|
||||
p_module text,
|
||||
p_trigger_event text,
|
||||
p_entity_id uuid,
|
||||
p_context jsonb DEFAULT '{}'
|
||||
)
|
||||
RETURNS void
|
||||
LANGUAGE plpgsql
|
||||
SECURITY DEFINER
|
||||
SET search_path = ''
|
||||
AS $$
|
||||
BEGIN
|
||||
INSERT INTO public.pending_module_notifications
|
||||
(account_id, module, trigger_event, entity_id, context)
|
||||
VALUES
|
||||
(p_account_id, p_module, p_trigger_event, p_entity_id, p_context);
|
||||
END;
|
||||
$$;
|
||||
|
||||
GRANT EXECUTE ON FUNCTION public.enqueue_module_notification(uuid, text, text, uuid, jsonb) TO authenticated;
|
||||
GRANT EXECUTE ON FUNCTION public.enqueue_module_notification(uuid, text, text, uuid, jsonb) TO service_role;
|
||||
Reference in New Issue
Block a user