feat(api): add contact form endpoint for website enquiries

Add POST /api/v1/contact endpoint to handle contact form submissions
from the marketing website. Includes:
- ContactSchema for validation with HTML sanitization
- Honeypot field for spam protection
- Rate limiting (5 per hour)
- Email notification to info@secuird.tech via NotificationService
This commit is contained in:
2026-04-17 15:55:19 +09:30
parent 7480e9d62b
commit 29d54ca109
4 changed files with 188 additions and 1 deletions
+66
View File
@@ -496,3 +496,69 @@ def build_email_verification_resend_html(
{get_alert_box("If you didn't request this, you can safely ignore this email.", "info", "🔒")}
'''
return get_base_html(content, "Verify your Secuird email address", "Please verify your email address")
def build_contact_enquiry_html(
enquiry_type: str,
submitter_email: str,
name: Optional[str],
company: Optional[str],
interest_area: Optional[str],
message: Optional[str],
) -> str:
"""Build a contact enquiry notification email.
Args:
enquiry_type: One of demo_request, sales_enquiry, general, support
submitter_email: Email address of the person submitting the enquiry
name: Full name of the submitter (optional)
company: Company name (optional)
interest_area: Area of interest (optional)
message: Free-text message (optional)
Returns:
HTML email string
"""
# Map enquiry types to display labels and colors
type_labels = {
"demo_request": ("Demo Request", "info"),
"sales_enquiry": ("Sales Enquiry", "success"),
"general": ("General Enquiry", "info"),
"support": ("Support Request", "warning"),
}
type_label, alert_type = type_labels.get(enquiry_type, ("Enquiry", "info"))
name_display = name if name else "Not provided"
company_display = company if company else "Not provided"
interest_display = interest_area if interest_area else "Not provided"
message_display = message if message else "No message provided"
# Build details table
details_rows = f"""
{get_detail_row("Enquiry Type", type_label)}
{get_detail_row("Submitter Email", submitter_email)}
{get_detail_row("Name", name_display)}
{get_detail_row("Company", company_display)}
{get_detail_row("Interest Area", interest_display)}
"""
content = f'''
<h2 style="margin: 0 0 20px 0; color: {TEXT_COLOR}; font-size: 20px; font-weight: 600;">New {type_label}</h2>
<p style="margin: 0 0 20px 0; color: {TEXT_COLOR}; font-size: 15px; line-height: 1.6;">
A new {type_label.lower()} has been submitted through the Secuird website.
</p>
{get_alert_box(f"Enquiry type: <strong>{type_label}</strong>", alert_type, "📬")}
<table role="presentation" width="100%" cellspacing="0" cellpadding="0" style="margin: 20px 0; background-color: {BACKGROUND_COLOR}; border-radius: 8px;">
<tr>
<td style="padding: 20px;">
<h3 style="margin: 0 0 16px 0; color: {TEXT_COLOR}; font-size: 14px; font-weight: 600;">Enquiry Details</h3>
<table role="presentation" width="100%" cellspacing="0" cellpadding="0">
{details_rows}
</table>
</td>
</tr>
</table>
<h3 style="margin: 20px 0 12px 0; color: {TEXT_COLOR}; font-size: 14px; font-weight: 600;">Message</h3>
<p style="margin: 0; color: {TEXT_COLOR}; font-size: 14px; line-height: 1.6; white-space: pre-wrap;">{message_display}</p>
'''
return get_base_html(content, f"Secuird Website: {type_label}", f"New {type_label} from {submitter_email}")