godcrm/backend/utils/email.js
GOD CRM Release f89e074dd1
Some checks failed
CI / Lint / Typecheck / Test / Build (push) Has been cancelled
CI / PostgreSQL Integration Tests (push) Has been cancelled
GOD CRM — public scrubbed snapshot
Governed substrate for autonomous agents: scoped identity (passports),
audited actions, MCP workspace. Infra IPs and secrets redacted for public release.
2026-08-10 04:01:45 +03:00

140 lines
4 KiB
JavaScript

import nodemailer from 'nodemailer';
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
import { logger } from './logger.js';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const SMTP_CONFIG_FILE = path.join(__dirname, '../smtp-config.json');
/**
* Send email with SMTP fallback logic
*/
export async function sendEmailWithFallback(to, subject, html) {
// Load SMTP accounts from config
let smtpAccounts = [];
try {
if (fs.existsSync(SMTP_CONFIG_FILE)) {
const data = fs.readFileSync(SMTP_CONFIG_FILE, 'utf8');
const config = JSON.parse(data);
smtpAccounts = config.accounts || [];
}
} catch (error) {
logger.error({ err: error }, 'Error loading SMTP config');
return { success: false, error: 'Failed to load SMTP configuration' };
}
if (smtpAccounts.length === 0) {
logger.error('❌ No SMTP accounts configured');
return { success: false, error: 'No SMTP accounts configured' };
}
// Try each SMTP account in order (PRIMARY first, then FALLBACKs)
for (let i = 0; i < smtpAccounts.length; i++) {
const account = smtpAccounts[i];
const accountLabel = i === 0 ? 'PRIMARY' : `FALLBACK #${i}`;
logger.info(`📧 Attempting to send email via ${accountLabel}: ${account.name} (${account.user})`);
try {
// Create transporter configuration
const transportConfig = {
host: account.host,
port: parseInt(account.port) || 587,
secure: parseInt(account.port) === 465,
tls: {
rejectUnauthorized: false
}
};
// Only add auth if user/password are provided
if (account.user && account.password) {
transportConfig.auth = {
user: account.user,
pass: account.password
};
}
const transporter = nodemailer.createTransport(transportConfig);
// Determine FROM address
let fromAddress;
if (account.fromEmail) {
fromAddress = account.fromEmail;
} else if (account.user) {
fromAddress = account.user;
} else if (account.host === '127.0.0.1' || account.host === 'localhost') {
fromAddress = 'noreply@hltrn.cc';
} else {
fromAddress = `noreply@${account.host.replace(/^(smtp\.|mail\.)/, '')}`;
}
const fromName = account.fromName || 'GOD CRM';
// Send email
const info = await transporter.sendMail({
from: `"${fromName}" <${fromAddress}>`,
to: to,
subject: subject,
html: html
});
logger.info(`✅ Email sent successfully via ${accountLabel}: ${account.name}`);
logger.info(` Message ID: ${info.messageId}`);
return {
success: true,
messageId: info.messageId,
usedAccount: account.name,
accountType: accountLabel
};
} catch (error) {
logger.error({ err: error }, `❌ Failed to send via ${accountLabel} (${account.name})`);
// If this is not the last account, try next one
if (i < smtpAccounts.length - 1) {
logger.warn(` ⚠️ Trying next SMTP account...`);
continue;
} else {
// All accounts failed
return {
success: false,
error: `All SMTP accounts failed. Last error: ${error.message}`
};
}
}
}
return { success: false, error: 'No SMTP accounts available' };
}
/**
* Load SMTP configuration
*/
export function loadSMTPConfig() {
try {
if (fs.existsSync(SMTP_CONFIG_FILE)) {
const data = fs.readFileSync(SMTP_CONFIG_FILE, 'utf8');
return JSON.parse(data);
}
return { accounts: [] };
} catch (error) {
logger.error({ err: error }, 'Error loading SMTP config');
return { accounts: [] };
}
}
/**
* Save SMTP configuration
*/
export function saveSMTPConfig(config) {
try {
fs.writeFileSync(SMTP_CONFIG_FILE, JSON.stringify(config, null, 2));
return { success: true };
} catch (error) {
logger.error({ err: error }, 'Error saving SMTP config');
return { success: false, error: error.message };
}
}