feat: Phase 6, Device provisioning and deployment of updates on git-pull

This commit is contained in:
2026-02-27 04:42:41 +02:00
parent 32a2634739
commit 57259c2c2f
19 changed files with 1670 additions and 26 deletions

87
backend/utils/email.py Normal file
View File

@@ -0,0 +1,87 @@
import logging
import resend
from config import settings
logger = logging.getLogger(__name__)
def _get_client() -> resend.Resend:
return resend.Resend(api_key=settings.resend_api_key)
def send_email(to: str, subject: str, html: str) -> None:
"""Send a transactional email via Resend. Logs errors but does not raise."""
try:
client = _get_client()
client.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,
customer_name: str | None = None,
) -> None:
"""Notify a customer that a Vesper device has been assigned to them."""
greeting = f"Hi {customer_name}," if customer_name else "Hello,"
html = f"""
<div style="font-family: sans-serif; max-width: 600px; margin: 0 auto;">
<h2 style="color: #111827;">Your Vesper device is ready</h2>
<p>{greeting}</p>
<p>A Vesper bell automation device has been registered and assigned to you.</p>
<p>
<strong>Serial Number:</strong>
<code style="background: #f3f4f6; padding: 2px 6px; border-radius: 4px; font-size: 14px;">{serial_number}</code>
</p>
<p>Open the Vesper app and enter this serial number to get started.</p>
<hr style="border: none; border-top: 1px solid #e5e7eb; margin: 24px 0;" />
<p style="color: #6b7280; font-size: 12px;">
If you did not expect this email, please contact your system administrator.
</p>
</div>
"""
send_email(
to=customer_email,
subject=f"Your Vesper device is ready — {serial_number}",
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,
)