Add platform SMTP and firm wizard invite email
This commit is contained in:
@@ -11,7 +11,7 @@ from typing import Any
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.modules.email_integration.models import EmailLog, EmailSetting, EmailTemplate
|
||||
from app.modules.email_integration.models import EmailLog, EmailSetting, EmailTemplate, PlatformEmailSetting
|
||||
from app.modules.email_integration.attachment_service import EmailAttachment
|
||||
from app.modules.core.iam.models import User
|
||||
from app.core.settings import get_settings
|
||||
@@ -232,6 +232,38 @@ def seed_default_email_templates(db: Session, tenant_id: int | None = None, bran
|
||||
existing_codes.add(code)
|
||||
|
||||
|
||||
|
||||
def get_platform_email_setting(db: Session) -> PlatformEmailSetting | None:
|
||||
return db.execute(
|
||||
select(PlatformEmailSetting).order_by(PlatformEmailSetting.id.asc())
|
||||
).scalars().first()
|
||||
|
||||
|
||||
def get_or_create_platform_email_setting(db: Session, actor_user_id: int | None = None) -> PlatformEmailSetting:
|
||||
row = get_platform_email_setting(db)
|
||||
if row:
|
||||
return row
|
||||
|
||||
settings = get_settings()
|
||||
row = PlatformEmailSetting(
|
||||
smtp_host=getattr(settings, "SMTP_HOST", None) or "mail.vavalam.com",
|
||||
smtp_port=int(getattr(settings, "SMTP_PORT", 465) or 465),
|
||||
smtp_security="SSL",
|
||||
smtp_username=getattr(settings, "SMTP_USERNAME", None) or "no-reply@vavalam.com",
|
||||
smtp_password=getattr(settings, "SMTP_PASSWORD", None),
|
||||
from_email=getattr(settings, "SMTP_FROM_EMAIL", None) or "no-reply@vavalam.com",
|
||||
from_name=getattr(settings, "SMTP_FROM_NAME", None) or "ERP Platform",
|
||||
reply_to_email=getattr(settings, "SMTP_FROM_EMAIL", None) or "no-reply@vavalam.com",
|
||||
send_auth_emails=True,
|
||||
is_active=True,
|
||||
created_by_user_id=actor_user_id,
|
||||
updated_by_user_id=actor_user_id,
|
||||
)
|
||||
db.add(row)
|
||||
db.flush()
|
||||
return row
|
||||
|
||||
|
||||
def get_email_setting(db: Session, tenant_id: int | None, branch_id: int | None) -> EmailSetting | None:
|
||||
if tenant_id is None:
|
||||
return None
|
||||
@@ -585,6 +617,123 @@ def process_pending_email_queue(
|
||||
db.flush()
|
||||
return result
|
||||
|
||||
|
||||
def send_platform_email_log_now(db: Session, log: EmailLog) -> EmailLog:
|
||||
"""Attempt immediate send through System Admin platform SMTP."""
|
||||
setting = get_platform_email_setting(db)
|
||||
now = datetime.now(timezone.utc)
|
||||
log.processing_started_at = now
|
||||
log.last_attempt_at = now
|
||||
log.attempt_count = int(getattr(log, "attempt_count", 0) or 0) + 1
|
||||
|
||||
if not setting or not setting.is_active:
|
||||
log.status = "SKIPPED"
|
||||
log.error_message = "Platform SMTP settings not configured or inactive."
|
||||
log.is_retryable = False
|
||||
log.processing_started_at = None
|
||||
db.flush()
|
||||
return log
|
||||
|
||||
try:
|
||||
_send_smtp(setting, log.recipient_email, log.subject, log.body or "", is_html=False)
|
||||
log.status = "SENT"
|
||||
log.sent_at = datetime.now(timezone.utc)
|
||||
log.error_message = None
|
||||
log.next_retry_at = None
|
||||
log.processing_started_at = None
|
||||
log.is_retryable = False
|
||||
except Exception as exc:
|
||||
log.status = "FAILED"
|
||||
log.error_message = str(exc)
|
||||
log.processing_started_at = None
|
||||
log.is_retryable = False
|
||||
log.next_retry_at = None
|
||||
db.flush()
|
||||
return log
|
||||
|
||||
|
||||
def send_platform_template_email(
|
||||
db: Session,
|
||||
*,
|
||||
recipient_email: str,
|
||||
template_code: str,
|
||||
context: dict[str, Any] | None = None,
|
||||
related_module: str | None = None,
|
||||
related_id: int | None = None,
|
||||
send_immediately: bool = True,
|
||||
queue_priority: int = 10,
|
||||
) -> EmailLog:
|
||||
"""Queue/send a platform email without tenant SMTP dependency."""
|
||||
context = dict(context or {})
|
||||
context.setdefault("firm_name", "ERP Platform")
|
||||
context.setdefault("support_email", "")
|
||||
|
||||
template = _get_template(db, None, None, template_code)
|
||||
if not template:
|
||||
subject = template_code
|
||||
body = ""
|
||||
else:
|
||||
subject = render_template_text(template.subject_template, context)
|
||||
body = render_template_text(template.body_template, context)
|
||||
|
||||
log = EmailLog(
|
||||
tenant_id=None,
|
||||
branch_id=None,
|
||||
recipient_email=recipient_email,
|
||||
subject=subject[:500],
|
||||
body=body,
|
||||
status="PENDING",
|
||||
related_module=related_module,
|
||||
related_id=related_id,
|
||||
template_code=template_code,
|
||||
queued_at=datetime.now(timezone.utc),
|
||||
max_attempts=1,
|
||||
queue_priority=int(queue_priority or 10),
|
||||
is_retryable=False,
|
||||
)
|
||||
db.add(log)
|
||||
db.flush()
|
||||
|
||||
setting = get_platform_email_setting(db)
|
||||
if not setting or not setting.is_active:
|
||||
log.status = "SKIPPED"
|
||||
log.error_message = "Platform SMTP settings not configured or inactive."
|
||||
db.flush()
|
||||
return log
|
||||
|
||||
if template_code.upper().startswith("AUTH_") and not getattr(setting, "send_auth_emails", True):
|
||||
log.status = "SKIPPED"
|
||||
log.error_message = "Platform authentication/invite emails are disabled."
|
||||
db.flush()
|
||||
return log
|
||||
|
||||
if send_immediately:
|
||||
send_platform_email_log_now(db, log)
|
||||
return log
|
||||
|
||||
|
||||
def send_platform_user_invite_email(db: Session, *, user: User, invite_token: str, firm_name: str | None = None) -> EmailLog | None:
|
||||
if not getattr(user, "email", None):
|
||||
return None
|
||||
invite_link = f"{_public_base_url()}/invite/accept?token={invite_token}"
|
||||
return send_platform_template_email(
|
||||
db,
|
||||
recipient_email=str(user.email),
|
||||
template_code="AUTH_USER_INVITE",
|
||||
context={
|
||||
"firm_name": firm_name or "ERP Platform",
|
||||
"user_name": _user_display_name(user),
|
||||
"user_email": str(user.email),
|
||||
"invite_link": invite_link,
|
||||
"expiry_hours": str(get_settings().INVITE_TOKEN_HOURS),
|
||||
"support_email": "",
|
||||
},
|
||||
related_module="wizard_firm_invite",
|
||||
related_id=int(user.id),
|
||||
queue_priority=5,
|
||||
)
|
||||
|
||||
|
||||
def send_template_email(
|
||||
db: Session,
|
||||
*,
|
||||
|
||||
Reference in New Issue
Block a user