feat: Add email logging and monitoring system

- Add EmailLog model to database.py for tracking sent emails
- Modify email_service.py to log all sent emails to database
- Track email type (welcome, password_reset, notification)
- Record sender, recipient, subject, status, timestamps
- Supports monitoring email delivery success/failure

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Maciej Pienczyn 2026-01-14 09:26:48 +01:00
parent 749abfa018
commit a09203ef55
2 changed files with 138 additions and 4 deletions

View File

@ -2525,6 +2525,54 @@ class PopularPagesDaily(Base):
return f"<PopularPagesDaily {self.date} {self.path}>"
# ============================================================
# 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"<EmailLog {self.id} {self.email_type} -> {self.recipient_email} ({self.status})>"
# ============================================================
# DATABASE INITIALIZATION
# ============================================================

View File

@ -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
</html>
"""
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
</html>
"""
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
)