const AWS = require('aws-sdk'); const nodemailer = require('nodemailer'); const axios = require('axios'); const crypto = require('crypto'); const config = require('../config'); // Инициализация AWS SES let sesClient = null; let transporter = null; const initializeEmailService = () => { const emailProvider = process.env.EMAIL_PROVIDER || 'aws'; // aws, yandex, smtp if (emailProvider === 'aws' && config.email?.aws) { const awsRegion = config.email.aws.region || 'us-east-1'; const validAWSRegions = [ 'us-east-1', 'us-east-2', 'us-west-1', 'us-west-2', 'eu-west-1', 'eu-west-2', 'eu-west-3', 'eu-central-1', 'ap-southeast-1', 'ap-southeast-2', 'ap-northeast-1', 'sa-east-1', 'ca-central-1' ]; // Проверка на Yandex Cloud Postbox (использует ru-central1) const isYandexCloud = awsRegion === 'ru-central1'; const endpointUrl = config.email.aws.endpoint || process.env.AWS_SES_ENDPOINT_URL || (isYandexCloud ? 'https://postbox.cloud.yandex.net' : null); const sesConfig = { accessKeyId: config.email.aws.accessKeyId, secretAccessKey: config.email.aws.secretAccessKey, region: awsRegion }; // Для Yandex Cloud Postbox нужен кастомный endpoint if (endpointUrl) { // Yandex Cloud Postbox использует SESv2 API, сохраняем конфигурацию для прямых запросов sesConfig.endpoint = endpointUrl; sesConfig.isYandexCloud = true; console.log(`[Email] Используется Yandex Cloud Postbox с endpoint: ${endpointUrl}`); // Не создаем SES клиент для Yandex Cloud, будем использовать прямые HTTP запросы } else if (!validAWSRegions.includes(awsRegion)) { console.warn(`[Email] Невалидный регион AWS SES: ${awsRegion}. Используется us-east-1`); sesConfig.region = 'us-east-1'; sesClient = new AWS.SES(sesConfig); } else { sesClient = new AWS.SES(sesConfig); } // Сохраняем конфигурацию для Yandex Cloud if (endpointUrl) { sesClient = { config: sesConfig, isYandexCloud: true }; } } else if (emailProvider === 'yandex' || emailProvider === 'smtp') { const emailConfig = config.email?.[emailProvider] || config.email?.smtp || {}; transporter = nodemailer.createTransport({ host: emailConfig.host || process.env.SMTP_HOST, port: emailConfig.port || parseInt(process.env.SMTP_PORT || '587', 10), secure: emailConfig.secure === true || process.env.SMTP_SECURE === 'true', auth: { user: emailConfig.user || process.env.SMTP_USER, pass: emailConfig.password || process.env.SMTP_PASSWORD } }); } }; // Генерация HTML письма с кодом const generateVerificationEmail = (code) => { return `

Код подтверждения

Ваш код для регистрации в Nakama:

${code}

Код действителен в течение 15 минут.

`; }; const sendEmail = async (to, subject, html, text) => { try { const emailProvider = process.env.EMAIL_PROVIDER || 'aws'; const fromEmail = process.env.EMAIL_FROM || config.email?.from || 'noreply@nakama.guru'; if (emailProvider === 'aws' && sesClient) { // Проверка на Yandex Cloud Postbox if (sesClient.isYandexCloud) { // Yandex Cloud Postbox использует SESv2 API - используем прямой HTTP запрос const endpoint = sesClient.config.endpoint; const payload = { FromEmailAddress: fromEmail, Destination: { ToAddresses: [to] }, Content: { Simple: { Subject: { Data: subject, Charset: 'UTF-8' }, Body: { Html: { Data: html, Charset: 'UTF-8' }, Text: { Data: text || html.replace(/<[^>]*>/g, ''), Charset: 'UTF-8' } } } } }; // Используем AWS SDK для создания подписи, но отправляем через axios // Создаем временный SES клиент для подписи запроса const tempSES = new AWS.SES({ accessKeyId: sesClient.config.accessKeyId, secretAccessKey: sesClient.config.secretAccessKey, region: sesClient.config.region, endpoint: endpoint }); // Пробуем использовать обычный SES API (может не работать) try { const params = { Source: fromEmail, Destination: { ToAddresses: [to] }, Message: { Subject: { Data: subject, Charset: 'UTF-8' }, Body: { Html: { Data: html, Charset: 'UTF-8' }, Text: { Data: text || html.replace(/<[^>]*>/g, ''), Charset: 'UTF-8' } } } }; const result = await tempSES.sendEmail(params).promise(); return { success: true, messageId: result.MessageId }; } catch (sesError) { // Если SES API не работает, пробуем через SMTP console.warn('[Email] SES API не работает с Yandex Cloud, используйте EMAIL_PROVIDER=smtp или yandex'); throw new Error('Yandex Cloud Postbox требует SESv2 API. Используйте EMAIL_PROVIDER=yandex или smtp'); } } else { // Обычный AWS SES const params = { Source: fromEmail, Destination: { ToAddresses: [to] }, Message: { Subject: { Data: subject, Charset: 'UTF-8' }, Body: { Html: { Data: html, Charset: 'UTF-8' }, Text: { Data: text || html.replace(/<[^>]*>/g, ''), Charset: 'UTF-8' } } } }; const result = await sesClient.sendEmail(params).promise(); return { success: true, messageId: result.MessageId }; } } else if (transporter) { // Отправка через SMTP (Yandex, Gmail и т.д.) const info = await transporter.sendMail({ from: fromEmail, to, subject, html, text: text || html.replace(/<[^>]*>/g, '') }); return { success: true, messageId: info.messageId }; } else { throw new Error('Email service not configured'); } } catch (error) { console.error('Ошибка отправки email:', error); throw error; } }; const sendVerificationCode = async (email, code) => { const subject = 'Код подтверждения регистрации - Nakama'; const html = generateVerificationEmail(code); const text = `Ваш код подтверждения: ${code}. Код действителен 15 минут.`; return await sendEmail(email, subject, html, text); }; // Генерация HTML письма с кодом для админа const generateAdminConfirmationEmail = (code, action, userInfo) => { const actionText = action === 'add' ? 'добавления админа' : 'удаления админа'; const userDetails = userInfo ? `

Пользователь: @${userInfo.username} (${userInfo.firstName})

${userInfo.adminNumber ? `

Номер админа: ${userInfo.adminNumber}

` : ''} ` : ''; return `

Подтверждение ${actionText}

${userDetails}

Код подтверждения:

${code}

Код действителен в течение 5 минут.

`; }; const sendAdminConfirmationCode = async (code, action, userInfo) => { const ownerEmail = config.ownerEmail || process.env.OWNER_EMAIL || 'aaem9848@gmail.com'; const actionText = action === 'add' ? 'добавления админа' : 'удаления админа'; const subject = `Код подтверждения ${actionText} - Nakama Moderation`; const html = generateAdminConfirmationEmail(code, action, userInfo); const text = `Код подтверждения ${actionText}: ${code}\n\nПользователь: @${userInfo?.username || 'не указан'}\nКод действителен 5 минут.`; return await sendEmail(ownerEmail, subject, html, text); }; // Инициализация при загрузке модуля initializeEmailService(); module.exports = { sendEmail, sendVerificationCode, sendAdminConfirmationCode, initializeEmailService };