-- ============================================ -- TEMPLATE-SAAS: Notifications -- Schema: notifications -- Version: 1.0.0 -- ============================================ -- Notification templates CREATE TABLE notifications.templates ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- Template identification code VARCHAR(100) UNIQUE NOT NULL, -- e.g., 'welcome_email', 'invoice_paid' name VARCHAR(200) NOT NULL, description TEXT, category VARCHAR(100), -- 'transactional', 'marketing', 'system' -- Channel channel notifications.channel NOT NULL, -- Content subject VARCHAR(500), -- For email body TEXT NOT NULL, body_html TEXT, -- For email HTML version -- Variables (for template rendering) variables JSONB DEFAULT '[]'::jsonb, -- Example: [{"name": "user_name", "required": true}, {"name": "company", "default": "SaaS"}] -- Status is_active BOOLEAN DEFAULT TRUE, -- Audit created_at TIMESTAMPTZ DEFAULT NOW() NOT NULL, updated_at TIMESTAMPTZ DEFAULT NOW() NOT NULL ); -- Notifications (instances) CREATE TABLE notifications.notifications ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), tenant_id UUID NOT NULL REFERENCES tenants.tenants(id) ON DELETE CASCADE, user_id UUID REFERENCES users.users(id) ON DELETE SET NULL, -- Template template_id UUID REFERENCES notifications.templates(id), template_code VARCHAR(100), -- Channel channel notifications.channel NOT NULL, -- Content subject VARCHAR(500), body TEXT NOT NULL, body_html TEXT, -- Recipient recipient_email VARCHAR(255), recipient_phone VARCHAR(50), recipient_device_token VARCHAR(500), -- Status status notifications.notification_status DEFAULT 'pending' NOT NULL, priority notifications.priority DEFAULT 'normal', -- Delivery info sent_at TIMESTAMPTZ, delivered_at TIMESTAMPTZ, is_read BOOLEAN DEFAULT FALSE NOT NULL, read_at TIMESTAMPTZ, failed_at TIMESTAMPTZ, failure_reason TEXT, -- Retry retry_count INT DEFAULT 0, next_retry_at TIMESTAMPTZ, -- Metadata metadata JSONB DEFAULT '{}'::jsonb, -- Audit created_at TIMESTAMPTZ DEFAULT NOW() NOT NULL ); -- User preferences CREATE TABLE notifications.user_preferences ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), tenant_id UUID NOT NULL REFERENCES tenants.tenants(id) ON DELETE CASCADE, user_id UUID NOT NULL REFERENCES users.users(id) ON DELETE CASCADE, -- Channel preferences email_enabled BOOLEAN DEFAULT TRUE, push_enabled BOOLEAN DEFAULT TRUE, sms_enabled BOOLEAN DEFAULT FALSE, in_app_enabled BOOLEAN DEFAULT TRUE, -- Category preferences (JSONB) category_preferences JSONB DEFAULT '{}'::jsonb, -- Example: { "marketing": false, "transactional": true } -- Quiet hours quiet_hours_enabled BOOLEAN DEFAULT FALSE, quiet_hours_start TIME, quiet_hours_end TIME, quiet_hours_timezone VARCHAR(50), -- Digest digest_enabled BOOLEAN DEFAULT FALSE, digest_frequency VARCHAR(20), -- 'daily', 'weekly' -- Audit created_at TIMESTAMPTZ DEFAULT NOW() NOT NULL, updated_at TIMESTAMPTZ DEFAULT NOW() NOT NULL, -- Constraints CONSTRAINT unique_user_preferences UNIQUE (user_id) ); -- Indexes CREATE INDEX idx_templates_code ON notifications.templates(code) WHERE is_active = TRUE; CREATE INDEX idx_templates_channel ON notifications.templates(channel) WHERE is_active = TRUE; CREATE INDEX idx_notifications_tenant ON notifications.notifications(tenant_id, created_at DESC); CREATE INDEX idx_notifications_user ON notifications.notifications(user_id, created_at DESC); CREATE INDEX idx_notifications_status ON notifications.notifications(status) WHERE status IN ('pending', 'sent'); CREATE INDEX idx_notifications_retry ON notifications.notifications(next_retry_at) WHERE status = 'pending' AND next_retry_at IS NOT NULL; CREATE INDEX idx_user_preferences_user ON notifications.user_preferences(user_id); -- RLS ALTER TABLE notifications.notifications ENABLE ROW LEVEL SECURITY; ALTER TABLE notifications.user_preferences ENABLE ROW LEVEL SECURITY; CREATE POLICY notifications_tenant_isolation ON notifications.notifications USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID); CREATE POLICY user_preferences_tenant_isolation ON notifications.user_preferences USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID); -- Trigger CREATE OR REPLACE FUNCTION notifications.update_updated_at() RETURNS TRIGGER AS $$ BEGIN NEW.updated_at = NOW(); RETURN NEW; END; $$ LANGUAGE plpgsql; CREATE TRIGGER trg_templates_updated_at BEFORE UPDATE ON notifications.templates FOR EACH ROW EXECUTE FUNCTION notifications.update_updated_at(); CREATE TRIGGER trg_user_preferences_updated_at BEFORE UPDATE ON notifications.user_preferences FOR EACH ROW EXECUTE FUNCTION notifications.update_updated_at(); -- Comments COMMENT ON TABLE notifications.templates IS 'Notification templates with variables'; COMMENT ON TABLE notifications.notifications IS 'Notification instances and delivery status'; COMMENT ON TABLE notifications.user_preferences IS 'User notification preferences';