Re_Backend/src/emailtemplates/emailPreferences.helper.ts

285 lines
9.2 KiB
TypeScript

/**
* 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<boolean> {
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<boolean> {
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<boolean> {
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<boolean> {
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<boolean> {
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<string[]> {
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<boolean> {
// 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);
}