Files
bellsystems-cp/backend/utils/email.py

154 lines
6.8 KiB
Python

import logging
import resend
from config import settings
logger = logging.getLogger(__name__)
def send_email(to: str, subject: str, html: str) -> None:
"""Send a transactional email via Resend. Logs errors but does not raise."""
try:
resend.api_key = settings.resend_api_key
resend.Emails.send({
"from": settings.email_from,
"to": to,
"subject": subject,
"html": html,
})
logger.info("Email sent to %s — subject: %s", to, subject)
except Exception as exc:
logger.error("Failed to send email to %s: %s", to, exc)
raise
def send_device_assignment_invite(
customer_email: str,
serial_number: str,
device_name: str,
customer_name: str | None = None,
) -> None:
"""Notify a customer that a Bell Systems device has been assigned and shipped to them."""
greeting = f"Dear {customer_name}," if customer_name else "Dear Customer,"
html = f"""
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"></head>
<body style="margin:0; padding:0; background-color:#f4f4f7; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;">
<table width="100%" cellpadding="0" cellspacing="0" style="background-color:#f4f4f7; padding: 40px 0;">
<tr>
<td align="center">
<table width="600" cellpadding="0" cellspacing="0" style="background-color:#ffffff; border-radius:8px; overflow:hidden; box-shadow: 0 2px 8px rgba(0,0,0,0.08); max-width:600px; width:100%;">
<!-- Header -->
<tr>
<td style="background-color:#0f172a; padding: 32px 40px; text-align:center;">
<h1 style="color:#ffffff; margin:0; font-size:22px; font-weight:700; letter-spacing:1px;">BELLSYSTEMS</h1>
<p style="color:#94a3b8; margin:6px 0 0; font-size:13px; letter-spacing:2px; text-transform:uppercase;">Device Shipment Confirmation</p>
</td>
</tr>
<!-- Body -->
<tr>
<td style="padding: 40px 40px 32px;">
<p style="margin:0 0 20px; font-size:16px; color:#1e293b;">{greeting}</p>
<p style="margin:0 0 16px; font-size:15px; color:#334155; line-height:1.7;">
Your <strong>Bell Systems {device_name}</strong> device has been successfully manufactured and shipped.
We are delighted to have it on its way to you!
</p>
<p style="margin:0 0 24px; font-size:15px; color:#334155; line-height:1.7;">
To get started, download our controller application from the Google Play Store and follow the in-app setup instructions.
</p>
<!-- CTA Button -->
<table cellpadding="0" cellspacing="0" width="100%" style="margin: 28px 0;">
<tr>
<td align="center">
<a href="https://play.google.com/store/apps/details?id=com.bellsystems.vesper"
style="display:inline-block; background-color:#0f172a; color:#ffffff; text-decoration:none;
padding:14px 32px; border-radius:6px; font-size:15px; font-weight:600; letter-spacing:0.5px;">
Download on Google Play
</a>
</td>
</tr>
</table>
<!-- Device info card -->
<table width="100%" cellpadding="0" cellspacing="0" style="background:#f8fafc; border:1px solid #e2e8f0; border-radius:6px; margin-bottom:28px;">
<tr>
<td style="padding:16px 20px; border-bottom:1px solid #e2e8f0;">
<span style="font-size:12px; color:#64748b; text-transform:uppercase; letter-spacing:1px; font-weight:600;">Device</span><br>
<span style="font-size:15px; color:#0f172a; font-weight:600;">Bell Systems {device_name}</span>
</td>
</tr>
<tr>
<td style="padding:16px 20px;">
<span style="font-size:12px; color:#64748b; text-transform:uppercase; letter-spacing:1px; font-weight:600;">Serial Number</span><br>
<code style="font-size:14px; color:#0f172a; background:#e2e8f0; padding:3px 8px; border-radius:4px; font-family:monospace;">{serial_number}</code>
</td>
</tr>
</table>
<p style="margin:0; font-size:15px; color:#334155; line-height:1.7;">
Thank you very much. We greatly appreciate your choice in our products.
</p>
</td>
</tr>
<!-- Footer -->
<tr>
<td style="background-color:#f8fafc; border-top:1px solid #e2e8f0; padding:24px 40px; text-align:center;">
<p style="margin:0 0 4px; font-size:14px; color:#0f172a; font-weight:700;">BellSystems.gr</p>
<p style="margin:0; font-size:12px; color:#94a3b8;">
If you did not expect this email, please contact us at
<a href="mailto:support@bellsystems.gr" style="color:#64748b;">support@bellsystems.gr</a>
</p>
</td>
</tr>
</table>
</td>
</tr>
</table>
</body>
</html>
"""
send_email(
to=customer_email,
subject=f"Your Bell Systems {device_name} is on its way! 🎉",
html=html,
)
def send_device_provisioned_alert(
admin_email: str,
serial_number: str,
hw_type: str,
) -> None:
"""Internal alert sent to an admin when a device reaches provisioned status."""
html = f"""
<div style="font-family: sans-serif; max-width: 600px; margin: 0 auto;">
<h2 style="color: #111827;">Device Provisioned</h2>
<p>A Vesper device has successfully provisioned and is ready to ship.</p>
<table style="border-collapse: collapse; width: 100%; margin-top: 16px;">
<tr>
<td style="padding: 6px 12px; font-weight: bold; background: #f9fafb;">Serial Number</td>
<td style="padding: 6px 12px; font-family: monospace;">{serial_number}</td>
</tr>
<tr>
<td style="padding: 6px 12px; font-weight: bold; background: #f9fafb;">Board Type</td>
<td style="padding: 6px 12px;">{hw_type.upper()}</td>
</tr>
</table>
<p style="margin-top: 24px;">
<a href="#" style="color: #2563eb;">View in Admin Console</a>
</p>
</div>
"""
send_email(
to=admin_email,
subject=f"[Vesper] Device provisioned — {serial_number}",
html=html,
)