// email-notifications.js const nodemailer = require('nodemailer'); const { getDb } = require('./database'); class EmailNotifications { constructor() { this.transporter = null; this.initialized = false; this.init(); } async init() { try { console.log('πŸ”§ Π˜Π½ΠΈΡ†ΠΈΠ°Π»ΠΈΠ·Π°Ρ†ΠΈΡ Email ΡƒΠ²Π΅Π΄ΠΎΠΌΠ»Π΅Π½ΠΈΠΉ...'); if (!process.env.YANDEX_EMAIL || !process.env.YANDEX_PASSWORD) { console.warn('⚠️ Настройки ЯндСкс ΠΏΠΎΡ‡Ρ‚Ρ‹ Π½Π΅ ΡƒΠΊΠ°Π·Π°Π½Ρ‹ Π² .env'); console.warn(' Email увСдомлСния Π±ΡƒΠ΄ΡƒΡ‚ ΠΎΡ‚ΠΊΠ»ΡŽΡ‡Π΅Π½Ρ‹'); this.initialized = false; return; } this.transporter = nodemailer.createTransport({ host: process.env.YANDEX_SMTP_HOST || 'smtp.yandex.ru', port: parseInt(process.env.YANDEX_SMTP_PORT) || 587, secure: process.env.YANDEX_SMTP_SECURE === 'true', auth: { user: process.env.YANDEX_EMAIL, pass: process.env.YANDEX_PASSWORD }, tls: { rejectUnauthorized: false } }); // ВСстируСм ΠΏΠΎΠ΄ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠ΅ await this.transporter.verify(); this.initialized = true; console.log('βœ… Email увСдомлСния ΠΈΠ½ΠΈΡ†ΠΈΠ°Π»ΠΈΠ·ΠΈΡ€ΠΎΠ²Π°Π½Ρ‹'); console.log(`πŸ“§ ΠžΡ‚ΠΏΡ€Π°Π²ΠΈΡ‚Π΅Π»ΡŒ: ${process.env.YANDEX_EMAIL}`); } catch (error) { console.error('❌ Ошибка ΠΈΠ½ΠΈΡ†ΠΈΠ°Π»ΠΈΠ·Π°Ρ†ΠΈΠΈ Email ΡƒΠ²Π΅Π΄ΠΎΠΌΠ»Π΅Π½ΠΈΠΉ:', error.message); this.initialized = false; } } async getUserNotificationSettings(userId) { if (!getDb) return null; return new Promise((resolve, reject) => { const db = getDb(); db.get(` SELECT us.*, u.email as user_email, u.name as user_name FROM user_settings us LEFT JOIN users u ON us.user_id = u.id WHERE us.user_id = ? `, [userId], (err, settings) => { if (err) { reject(err); } else { resolve(settings); } }); }); } async saveUserNotificationSettings(userId, settings) { if (!getDb) return false; return new Promise((resolve, reject) => { const db = getDb(); const { email_notifications = true, notification_email = '', telegram_notifications = false, telegram_chat_id = '', vk_notifications = false, vk_user_id = '' } = settings; // ΠŸΡ€ΠΎΠ²Π΅Ρ€ΡΠ΅ΠΌ сущСствованиС записи db.get("SELECT id FROM user_settings WHERE user_id = ?", [userId], (err, existing) => { if (err) { reject(err); return; } if (existing) { // ОбновляСм ΡΡƒΡ‰Π΅ΡΡ‚Π²ΡƒΡŽΡ‰ΡƒΡŽ запись db.run(` UPDATE user_settings SET email_notifications = ?, notification_email = ?, telegram_notifications = ?, telegram_chat_id = ?, vk_notifications = ?, vk_user_id = ?, updated_at = CURRENT_TIMESTAMP WHERE user_id = ? `, [ email_notifications ? 1 : 0, notification_email, telegram_notifications ? 1 : 0, telegram_chat_id, vk_notifications ? 1 : 0, vk_user_id, userId ], function(err) { if (err) reject(err); else resolve(true); }); } else { // Π‘ΠΎΠ·Π΄Π°Π΅ΠΌ Π½ΠΎΠ²ΡƒΡŽ запись db.run(` INSERT INTO user_settings ( user_id, email_notifications, notification_email, telegram_notifications, telegram_chat_id, vk_notifications, vk_user_id ) VALUES (?, ?, ?, ?, ?, ?, ?) `, [ userId, email_notifications ? 1 : 0, notification_email, telegram_notifications ? 1 : 0, telegram_chat_id, vk_notifications ? 1 : 0, vk_user_id ], function(err) { if (err) reject(err); else resolve(true); }); } }); }); } async sendEmailNotification(to, subject, htmlContent) { if (!this.initialized || !this.transporter) { console.warn('⚠️ Email увСдомлСния ΠΎΡ‚ΠΊΠ»ΡŽΡ‡Π΅Π½Ρ‹'); return false; } try { const info = await this.transporter.sendMail({ from: `"School CRM" <${process.env.YANDEX_EMAIL}>`, to: to, subject: subject, html: htmlContent, text: htmlContent.replace(/<[^>]*>/g, '') // ΠšΠΎΠ½Π²Π΅Ρ€Ρ‚ΠΈΡ€ΡƒΠ΅ΠΌ HTML Π² тСкст }); console.log(`πŸ“§ Email ΠΎΡ‚ΠΏΡ€Π°Π²Π»Π΅Π½: ${to}, Message ID: ${info.messageId}`); return true; } catch (error) { console.error('❌ Ошибка ΠΎΡ‚ΠΏΡ€Π°Π²ΠΊΠΈ email:', error.message); return false; } } async sendTaskNotification(userId, taskData, notificationType) { try { const settings = await this.getUserNotificationSettings(userId); if (!settings || !settings.email_notifications) { return false; } // Π˜ΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅ΠΌ ΡƒΠΊΠ°Π·Π°Π½Π½ΡƒΡŽ email ΠΈΠ»ΠΈ email ΠΈΠ· профиля ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ const emailTo = settings.notification_email || settings.user_email; if (!emailTo) { console.log(`⚠️ Π£ ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ ${userId} Π½Π΅ ΡƒΠΊΠ°Π·Π°Π½ email для ΡƒΠ²Π΅Π΄ΠΎΠΌΠ»Π΅Π½ΠΈΠΉ`); return false; } let subject = ''; let htmlContent = ''; switch (notificationType) { case 'created': subject = `Новая Π·Π°Π΄Π°Ρ‡Π°: ${taskData.title}`; htmlContent = this.getTaskCreatedHtml(taskData); break; case 'updated': subject = `ОбновлСна Π·Π°Π΄Π°Ρ‡Π°: ${taskData.title}`; htmlContent = this.getTaskUpdatedHtml(taskData); break; case 'rework': subject = `Π—Π°Π΄Π°Ρ‡Π° Π²ΠΎΠ·Π²Ρ€Π°Ρ‰Π΅Π½Π° Π½Π° Π΄ΠΎΡ€Π°Π±ΠΎΡ‚ΠΊΡƒ: ${taskData.title}`; htmlContent = this.getTaskReworkHtml(taskData); break; case 'closed': subject = `Π—Π°Π΄Π°Ρ‡Π° Π·Π°ΠΊΡ€Ρ‹Ρ‚Π°: ${taskData.title}`; htmlContent = this.getTaskClosedHtml(taskData); break; case 'status_changed': subject = `ИзмСнСн статус Π·Π°Π΄Π°Ρ‡ΠΈ: ${taskData.title}`; htmlContent = this.getStatusChangedHtml(taskData); break; case 'deadline': subject = `Π‘ΠΊΠΎΡ€ΠΎ срок выполнСния: ${taskData.title}`; htmlContent = this.getDeadlineHtml(taskData); break; default: subject = `Π£Π²Π΅Π΄ΠΎΠΌΠ»Π΅Π½ΠΈΠ΅ ΠΏΠΎ Π·Π°Π΄Π°Ρ‡Π΅: ${taskData.title}`; htmlContent = this.getDefaultHtml(taskData); } return await this.sendEmailNotification(emailTo, subject, htmlContent); } catch (error) { console.error('❌ Ошибка ΠΎΡ‚ΠΏΡ€Π°Π²ΠΊΠΈ увСдомлСния ΠΎ Π·Π°Π΄Π°Ρ‡Π΅:', error); return false; } } // HTML ΡˆΠ°Π±Π»ΠΎΠ½Ρ‹ для Ρ€Π°Π·Π½Ρ‹Ρ… Ρ‚ΠΈΠΏΠΎΠ² ΡƒΠ²Π΅Π΄ΠΎΠΌΠ»Π΅Π½ΠΈΠΉ getTaskCreatedHtml(taskData) { return `

πŸ“‹ Новая Π·Π°Π΄Π°Ρ‡Π°

${taskData.title}

ОписаниС: ${taskData.description || 'Π‘Π΅Π· описания'}

Π‘Ρ€ΠΎΠΊ выполнСния: ${new Date(taskData.due_date).toLocaleString('ru-RU')}

Π‘ΠΎΠ·Π΄Π°Π»: ${taskData.author_name || 'НСизвСстно'}

Для просмотра ΠΏΠΎΠ΄Ρ€ΠΎΠ±Π½ΠΎΠΉ ΠΈΠ½Ρ„ΠΎΡ€ΠΌΠ°Ρ†ΠΈΠΈ ΠΏΠ΅Ρ€Π΅ΠΉΠ΄ΠΈΡ‚Π΅ Π² систСму управлСния Π·Π°Π΄Π°Ρ‡Π°ΠΌΠΈ.

ΠŸΠ΅Ρ€Π΅ΠΉΡ‚ΠΈ Π² CRM
`; } getTaskUpdatedHtml(taskData) { return `

πŸ”„ ОбновлСна Π·Π°Π΄Π°Ρ‡Π°

${taskData.title}

ИзмСнСния внСс: ${taskData.author_name || 'НСизвСстно'}

ВрСмя: ${new Date().toLocaleString('ru-RU')}

Для просмотра ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠΉ ΠΏΠ΅Ρ€Π΅ΠΉΠ΄ΠΈΡ‚Π΅ Π² систСму управлСния Π·Π°Π΄Π°Ρ‡Π°ΠΌΠΈ.

ΠŸΠ΅Ρ€Π΅ΠΉΡ‚ΠΈ Π² CRM
`; } getTaskReworkHtml(taskData) { return `

πŸ”„ Π—Π°Π΄Π°Ρ‡Π° Π²ΠΎΠ·Π²Ρ€Π°Ρ‰Π΅Π½Π° Π½Π° Π΄ΠΎΡ€Π°Π±ΠΎΡ‚ΠΊΡƒ

${taskData.title}

Автор замСчания: ${taskData.author_name || 'НСизвСстно'}

ΠšΠΎΠΌΠΌΠ΅Π½Ρ‚Π°Ρ€ΠΈΠΉ:

${taskData.comment || 'ВрСбуСтся Π΄ΠΎΡ€Π°Π±ΠΎΡ‚ΠΊΠ°'}

ΠŸΠΎΠΆΠ°Π»ΡƒΠΉΡΡ‚Π°, ΠΈΡΠΏΡ€Π°Π²ΡŒΡ‚Π΅ замСчания ΠΈ ΠΎΠ±Π½ΠΎΠ²ΠΈΡ‚Π΅ статус Π·Π°Π΄Π°Ρ‡ΠΈ.

ΠŸΠ΅Ρ€Π΅ΠΉΡ‚ΠΈ ΠΊ Π·Π°Π΄Π°Ρ‡Π΅
`; } getTaskClosedHtml(taskData) { return `

βœ… Π—Π°Π΄Π°Ρ‡Π° Π·Π°ΠΊΡ€Ρ‹Ρ‚Π°

${taskData.title}

Π—Π°ΠΊΡ€Ρ‹Ρ‚Π°: ${taskData.author_name || 'НСизвСстно'}

ВрСмя закрытия: ${new Date().toLocaleString('ru-RU')}

Π—Π°Π΄Π°Ρ‡Π° Π·Π°Π²Π΅Ρ€ΡˆΠ΅Π½Π° ΠΈ ΠΏΠ΅Ρ€Π΅ΠΌΠ΅Ρ‰Π΅Π½Π° Π² Π°Ρ€Ρ…ΠΈΠ².

ΠŸΠ΅Ρ€Π΅ΠΉΡ‚ΠΈ Π² CRM
`; } getStatusChangedHtml(taskData) { return `

πŸ”„ ИзмСнСн статус Π·Π°Π΄Π°Ρ‡ΠΈ

${taskData.title}

Новый статус: ${this.getStatusText(taskData.status)}

ИзмСнил: ${taskData.user_name || taskData.author_name || 'НСизвСстно'}

ВрСмя: ${new Date().toLocaleString('ru-RU')}

Для просмотра Π΄Π΅Ρ‚Π°Π»Π΅ΠΉ ΠΏΠ΅Ρ€Π΅ΠΉΠ΄ΠΈΡ‚Π΅ Π² систСму управлСния Π·Π°Π΄Π°Ρ‡Π°ΠΌΠΈ.

ΠŸΠ΅Ρ€Π΅ΠΉΡ‚ΠΈ Π² CRM
`; } getDeadlineHtml(taskData) { return `

⚠️ Π‘ΠΊΠΎΡ€ΠΎ срок выполнСния

${taskData.title}

Π’ΠΠ˜ΠœΠΠΠ˜Π•! Π”ΠΎ окончания срока Π·Π°Π΄Π°Ρ‡ΠΈ ΠΎΡΡ‚Π°Π»ΠΎΡΡŒ ΠΌΠ΅Π½Π΅Π΅ ${taskData.hours_left} часов!

Π‘Ρ€ΠΎΠΊ выполнСния: ${new Date(taskData.due_date).toLocaleString('ru-RU')}

ΠŸΠΎΠΆΠ°Π»ΡƒΠΉΡΡ‚Π°, Π·Π°Π²Π΅Ρ€ΡˆΠΈΡ‚Π΅ Π·Π°Π΄Π°Ρ‡Ρƒ Π² ΡƒΠΊΠ°Π·Π°Π½Π½Ρ‹ΠΉ срок.

ΠŸΠ΅Ρ€Π΅ΠΉΡ‚ΠΈ ΠΊ Π·Π°Π΄Π°Ρ‡Π΅
`; } getStatusText(status) { const statusMap = { 'assigned': 'НазначСна', 'in_progress': 'Π’ Ρ€Π°Π±ΠΎΡ‚Π΅', 'completed': 'Π—Π°Π²Π΅Ρ€ΡˆΠ΅Π½Π°', 'overdue': 'ΠŸΡ€ΠΎΡΡ€ΠΎΡ‡Π΅Π½Π°', 'rework': 'На Π΄ΠΎΡ€Π°Π±ΠΎΡ‚ΠΊΠ΅' }; return statusMap[status] || status; } getDefaultHtml(taskData) { return `

πŸ“’ Π£Π²Π΅Π΄ΠΎΠΌΠ»Π΅Π½ΠΈΠ΅ ΠΎΡ‚ School CRM

${taskData.title}

${taskData.message || 'НовоС ΡƒΠ²Π΅Π΄ΠΎΠΌΠ»Π΅Π½ΠΈΠ΅ ΠΏΠΎ Π·Π°Π΄Π°Ρ‡Π΅'}

ΠŸΠ΅Ρ€Π΅ΠΉΡ‚ΠΈ Π² CRM
`; } isReady() { return this.initialized; } } // Singleton const emailNotifications = new EmailNotifications(); module.exports = emailNotifications;