import smtplib from email.mime.text import MIMEText from email.mime.multipart import MIMEMultipart from email.mime.application import MIMEApplication from typing import List, Optional from datetime import datetime from app.core.config import settings from app.core.logging import migration_logger class EmailSender: """Класс для отправки email уведомлений""" def __init__(self): self.smtp_server = settings.EMAIL_HOST self.smtp_port = settings.EMAIL_PORT self.username = settings.EMAIL_USER self.password = settings.EMAIL_PASSWORD self.from_addr = settings.EMAIL_FROM self.to_addrs = settings.EMAIL_TO def send_email(self, subject: str, body: str, attachments: Optional[List[str]] = None) -> bool: """Отправка email с вложениями""" if not all([self.smtp_server, self.username, self.password, self.from_addr]): migration_logger.warning("Настройки email не заполнены. Отправка email пропущена.") return False try: # Создаем сообщение msg = MIMEMultipart() msg['From'] = self.from_addr msg['To'] = ', '.join(self.to_addrs) msg['Subject'] = subject msg['Date'] = datetime.now().strftime('%a, %d %b %Y %H:%M:%S %z') # Добавляем текст msg.attach(MIMEText(body, 'plain', 'utf-8')) # Добавляем вложения if attachments: for file_path in attachments: try: with open(file_path, 'rb') as f: part = MIMEApplication(f.read(), Name=file_path.split('/')[-1]) part['Content-Disposition'] = f'attachment; filename="{file_path.split("/")[-1]}"' msg.attach(part) except Exception as e: migration_logger.error(f"Ошибка при добавлении вложения {file_path}: {e}") # Отправляем with smtplib.SMTP_SSL(self.smtp_server, self.smtp_port) as server: server.login(self.username, self.password) server.send_message(msg) migration_logger.info(f"Email успешно отправлен на {', '.join(self.to_addrs)}") return True except Exception as e: migration_logger.error(f"Ошибка при отправке email: {e}") return False def send_error_notification(self, error_message: str, traceback_str: str = None, table_name: str = None): """Отправить уведомление об ошибке""" subject = f"ОШИБКА МИГРАЦИИ - {datetime.now().strftime('%Y-%m-%d %H:%M')}" body = f""" 🚨 ОШИБКА В ПРОЦЕССЕ МИГРАЦИИ ДАННЫХ {'='*60} Время: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} Сервис: Migration Service """ if table_name: body += f"Таблица: {table_name}\n" body += f""" Ошибка: {error_message} """ if traceback_str: body += f""" 📎 Детали: {traceback_str} """ body += """ 🔧 Необходимо проверить логи и устранить проблему. """ return self.send_email(subject, body) def send_success_notification(self, stats: dict, duration: float): """Отправить уведомление об успешной миграции""" subject = f"УСПЕШНАЯ МИГРАЦИЯ - {datetime.now().strftime('%Y-%m-%d %H:%M')}" body = f""" МИГРАЦИЯ ДАННЫХ УСПЕШНО ЗАВЕРШЕНА {'='*60} Время: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} Длительность: {duration:.1f} сек СТАТИСТИКА: {'='*40} Всего строк в БД: {stats.get('total_rows', 0)} Таблиц обработано: {stats.get('total_tables', 0)} Последняя репликация: {stats.get('last_replication', 'Нет данных')} Миграция выполнена успешно! """ return self.send_email(subject, body) def send_start_notification(self, tables: List[str], full_reload: bool): """Отправить уведомление о начале миграции""" subject = f"НАЧАЛО МИГРАЦИИ - {datetime.now().strftime('%Y-%m-%d %H:%M')}" body = f""" НАЧАЛО МИГРАЦИИ ДАННЫХ {'='*60} Время старта: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} Режим: {'ПОЛНАЯ ПЕРЕЗАГРУЗКА' if full_reload else 'ИНКРЕМЕНТАЛЬНАЯ'} Таблиц для обработки: {len(tables)} СПИСОК ТАБЛИЦ: {chr(10).join([f' • {t}' for t in tables])} Миграция запущена... """ return self.send_email(subject, body) email_sender = EmailSender()