/** * Email Preferences Helper * * Handles admin-level and user-level email notification preferences * Logic: Email only sent if BOTH admin AND user have it enabled */ import { User } from '@models/User'; import { SYSTEM_CONFIG } from '../config/system.config'; import { getConfigValue } from '../services/configReader.service'; import logger from '../utils/logger'; /** * Email notification types that can be controlled */ export enum EmailNotificationType { REQUEST_CREATED = 'request_created', APPROVAL_REQUEST = 'approval_request', REQUEST_APPROVED = 'request_approved', REQUEST_REJECTED = 'request_rejected', TAT_REMINDER = 'tat_reminder', // Generic TAT reminder (any threshold) TAT_BREACHED = 'tat_breached', // 100% breach WORKFLOW_RESUMED = 'workflow_resumed', REQUEST_CLOSED = 'request_closed', WORKFLOW_PAUSED = 'workflow_paused', PARTICIPANT_ADDED = 'participant_added', SPECTATOR_ADDED = 'spectator_added', APPROVER_SKIPPED = 'approver_skipped', // Dealer Claim Specific DEALER_PROPOSAL_SUBMITTED = 'dealer_proposal_submitted', ACTIVITY_CREATED = 'activity_created', COMPLETION_DOCUMENTS_SUBMITTED = 'completion_documents_submitted', EINVOICE_GENERATED = 'einvoice_generated', CREDIT_NOTE_SENT = 'credit_note_sent' } /** * Check if email should be sent based on admin and user preferences * * @param userId - User ID to check preferences for * @param emailType - Type of email notification * @returns true if email should be sent, false otherwise */ export async function shouldSendEmail( userId: string, emailType: EmailNotificationType ): Promise { try { // Step 1: Check admin-level configuration (System Config) const adminEmailEnabled = await isAdminEmailEnabled(emailType); if (!adminEmailEnabled) { logger.info(`[Email] Admin disabled emails for ${emailType} - skipping`); return false; } // Step 2: Check user-level preferences const userEmailEnabled = await isUserEmailEnabled(userId, emailType); if (!userEmailEnabled) { logger.info(`[Email] User ${userId} disabled emails for ${emailType} - skipping`); return false; } // Both admin AND user have enabled - send email logger.info(`[Email] Email enabled for user ${userId}, type: ${emailType}`); return true; } catch (error) { logger.error(`[Email] Error checking email preferences for ${userId}:`, error); // On error, default to NOT sending email (safe default) return false; } } /** * Check if admin has enabled emails globally * Checks database configuration first, then falls back to environment variable */ async function isAdminEmailEnabled(emailType: EmailNotificationType): Promise { try { // Step 1: Check database configuration (admin panel setting) const dbConfigValue = await getConfigValue('ENABLE_EMAIL_NOTIFICATIONS', ''); if (dbConfigValue) { // Parse database value (it's stored as string 'true' or 'false') const dbEnabled = dbConfigValue.toLowerCase() === 'true'; if (!dbEnabled) { logger.info('[Email] Admin has disabled email notifications globally (from database config)'); return false; } logger.debug('[Email] Email notifications enabled (from database config)'); return true; } // Step 2: Fall back to environment variable if database config not found const envEnabled = SYSTEM_CONFIG.NOTIFICATIONS.ENABLE_EMAIL; if (!envEnabled) { logger.info('[Email] Admin has disabled email notifications globally (from environment variable)'); return false; } logger.debug('[Email] Email notifications enabled (from environment variable)'); return true; } catch (error) { logger.error('[Email] Error checking admin email configuration, defaulting to enabled:', error); // On error, default to enabled (safe default to avoid blocking notifications) return true; } } /** * Check if user has enabled emails * Uses existing User.emailNotificationsEnabled field */ async function isUserEmailEnabled(userId: string, emailType: EmailNotificationType): Promise { try { // Fetch user and check emailNotificationsEnabled field const user = await User.findByPk(userId, { attributes: ['userId', 'emailNotificationsEnabled'] }); if (!user) { logger.warn(`[Email] User ${userId} not found - defaulting to enabled`); return true; } // Check user's global email notification setting const enabled = (user as any).emailNotificationsEnabled !== false; if (!enabled) { logger.info(`[Email] User ${userId} has disabled email notifications globally`); } return enabled; } catch (error) { logger.warn('[Email] Error checking user email preference (defaulting to enabled):', error); return true; } } /** * Check if in-app notification should be sent * Uses existing User.inAppNotificationsEnabled field */ export async function shouldSendInAppNotification( userId: string, notificationType: string ): Promise { try { // Check admin config first (if SystemConfig model exists) const adminEnabled = await isAdminInAppEnabled(notificationType); if (!adminEnabled) { return false; } // Fetch user and check inAppNotificationsEnabled field const user = await User.findByPk(userId, { attributes: ['userId', 'inAppNotificationsEnabled'] }); if (!user) { logger.warn(`[Notification] User ${userId} not found - defaulting to enabled`); return true; } // Check user's global in-app notification setting const enabled = (user as any).inAppNotificationsEnabled !== false; if (!enabled) { logger.info(`[Notification] User ${userId} has disabled in-app notifications globally`); } return enabled; } catch (error) { logger.warn('[Notification] Error checking in-app notification preference (defaulting to enabled):', error); return true; } } /** * Check if admin has enabled in-app notifications globally * Checks database configuration first, then falls back to environment variable */ async function isAdminInAppEnabled(notificationType: string): Promise { try { // Step 1: Check database configuration (admin panel setting) const dbConfigValue = await getConfigValue('ENABLE_IN_APP_NOTIFICATIONS', ''); if (dbConfigValue) { // Parse database value (it's stored as string 'true' or 'false') const dbEnabled = dbConfigValue.toLowerCase() === 'true'; if (!dbEnabled) { logger.info('[Notification] Admin has disabled in-app notifications globally (from database config)'); return false; } logger.debug('[Notification] In-app notifications enabled (from database config)'); return true; } // Step 2: Fall back to environment variable if database config not found const envValue = process.env.ENABLE_IN_APP_NOTIFICATIONS; if (envValue !== undefined) { const envEnabled = envValue.toLowerCase() === 'true'; if (!envEnabled) { logger.info('[Notification] Admin has disabled in-app notifications globally (from environment variable)'); return false; } logger.debug('[Notification] In-app notifications enabled (from environment variable)'); return true; } // Step 3: Final fallback to system config (defaults to true) const adminInAppEnabled = SYSTEM_CONFIG.NOTIFICATIONS.ENABLE_IN_APP; if (!adminInAppEnabled) { logger.info('[Notification] Admin has disabled in-app notifications globally (from system config)'); return false; } logger.debug('[Notification] In-app notifications enabled (from system config default)'); return true; } catch (error) { logger.error('[Notification] Error checking admin in-app notification configuration, defaulting to enabled:', error); // On error, default to enabled (safe default to avoid blocking notifications) return true; } } /** * Batch check for multiple users * Returns array of user IDs who should receive the email */ export async function filterUsersForEmail( userIds: string[], emailType: EmailNotificationType ): Promise { const enabledUsers: string[] = []; for (const userId of userIds) { const shouldSend = await shouldSendEmail(userId, emailType); if (shouldSend) { enabledUsers.push(userId); } } return enabledUsers; } /** * Critical emails that should ALWAYS be sent (override preferences) * These are too important to be disabled */ export const CRITICAL_EMAILS = [ EmailNotificationType.REQUEST_REJECTED, EmailNotificationType.TAT_BREACHED ]; /** * Check if email should be sent, with critical email override */ export async function shouldSendEmailWithOverride( userId: string, emailType: EmailNotificationType ): Promise { // Critical emails always sent (override user preference) if (CRITICAL_EMAILS.includes(emailType)) { const adminEnabled = await isAdminEmailEnabled(emailType); if (adminEnabled) { logger.info(`[Email] Critical email ${emailType} - sending despite user preference`); return true; } } // Non-critical emails - check both admin and user preferences return await shouldSendEmail(userId, emailType); }