diff --git a/database.py b/database.py index acead0e..c81bd97 100644 --- a/database.py +++ b/database.py @@ -2525,6 +2525,54 @@ class PopularPagesDaily(Base): return f"" +# ============================================================ +# EMAIL LOGGING +# ============================================================ + +class EmailLog(Base): + """ + Log wszystkich wysłanych emaili systemowych. + + Śledzi: + - Emaile rejestracyjne (weryfikacja) + - Emaile resetowania hasła + - Powiadomienia systemowe + - Status dostarczenia + + Created: 2026-01-14 + """ + __tablename__ = 'email_logs' + + id = Column(Integer, primary_key=True) + + # Dane emaila + email_type = Column(String(50), nullable=False, index=True) # welcome, password_reset, notification + recipient_email = Column(String(255), nullable=False, index=True) + recipient_name = Column(String(255), nullable=True) + subject = Column(String(500), nullable=False) + + # Powiązanie z użytkownikiem (opcjonalne) + user_id = Column(Integer, ForeignKey('users.id', ondelete='SET NULL'), nullable=True) + + # Status + status = Column(String(20), default='pending', index=True) # pending, sent, failed + error_message = Column(Text, nullable=True) + + # Metadane + sender_email = Column(String(255), nullable=True) + ip_address = Column(String(45), nullable=True) # IP requestu (jeśli dostępne) + + # Timestamps + created_at = Column(DateTime, default=datetime.utcnow) + sent_at = Column(DateTime, nullable=True) + + # Relacje + user = relationship('User', backref='email_logs') + + def __repr__(self): + return f" {self.recipient_email} ({self.status})>" + + # ============================================================ # DATABASE INITIALIZATION # ============================================================ diff --git a/email_service.py b/email_service.py index 56356fc..7dcf527 100644 --- a/email_service.py +++ b/email_service.py @@ -204,7 +204,10 @@ def send_email( subject: str, body_text: str, body_html: Optional[str] = None, - from_address: Optional[str] = None + from_address: Optional[str] = None, + email_type: str = 'notification', + user_id: Optional[int] = None, + recipient_name: Optional[str] = None ) -> bool: """ Send email using the global Email Service instance @@ -215,6 +218,9 @@ def send_email( body_text: Plain text email body body_html: HTML email body (optional) from_address: Sender email (optional) + email_type: Type of email for logging (welcome, password_reset, notification) + user_id: User ID for logging (optional) + recipient_name: Recipient name for logging (optional) Returns: True if sent successfully, False otherwise @@ -227,7 +233,74 @@ def send_email( if isinstance(to, str): to = [to] - return _email_service.send_mail(to, subject, body_text, body_html, from_address) + result = _email_service.send_mail(to, subject, body_text, body_html, from_address) + + # Log email to database + _log_email( + email_type=email_type, + recipient_emails=to, + recipient_name=recipient_name, + subject=subject, + user_id=user_id, + sender_email=from_address or _email_service.mail_from if _email_service else None, + success=result + ) + + return result + + +def _log_email( + email_type: str, + recipient_emails: List[str], + subject: str, + success: bool, + recipient_name: Optional[str] = None, + user_id: Optional[int] = None, + sender_email: Optional[str] = None, + error_message: Optional[str] = None +) -> None: + """ + Log email to database for monitoring. + + Args: + email_type: Type of email (welcome, password_reset, notification) + recipient_emails: List of recipient email addresses + subject: Email subject + success: Whether email was sent successfully + recipient_name: Recipient name (optional) + user_id: User ID (optional) + sender_email: Sender email address (optional) + error_message: Error message if failed (optional) + """ + try: + from database import SessionLocal, EmailLog + from datetime import datetime + + db = SessionLocal() + try: + for email in recipient_emails: + log_entry = EmailLog( + email_type=email_type, + recipient_email=email, + recipient_name=recipient_name, + subject=subject, + user_id=user_id, + sender_email=sender_email, + status='sent' if success else 'failed', + sent_at=datetime.utcnow() if success else None, + error_message=error_message if not success else None + ) + db.add(log_entry) + db.commit() + logger.info(f"Email logged: {email_type} -> {recipient_emails} (status: {'sent' if success else 'failed'})") + except Exception as e: + db.rollback() + logger.error(f"Failed to log email to database: {e}") + finally: + db.close() + except ImportError: + # Database not available (e.g., during testing) + logger.warning("Could not log email - database module not available") def is_configured() -> bool: @@ -325,7 +398,13 @@ https://nordabiznes.pl """ - return send_email([email], subject, body_text, body_html) + return send_email( + to=[email], + subject=subject, + body_text=body_text, + body_html=body_html, + email_type='password_reset' + ) def send_welcome_email(email: str, name: str, verification_url: str) -> bool: @@ -417,4 +496,11 @@ https://nordabiznes.pl """ - return send_email([email], subject, body_text, body_html) + return send_email( + to=[email], + subject=subject, + body_text=body_text, + body_html=body_html, + email_type='welcome', + recipient_name=name + )