136 lines
4.1 KiB
TypeScript
136 lines
4.1 KiB
TypeScript
import { z } from 'zod'
|
|
|
|
const contactSchema = z.object({
|
|
name: z.string().min(1).max(100),
|
|
email: z.string().email(),
|
|
message: z.string().min(10).max(5000)
|
|
})
|
|
|
|
export default defineEventHandler(async (event) => {
|
|
const config = useRuntimeConfig()
|
|
|
|
// Validate request body
|
|
const body = await readBody(event)
|
|
const result = contactSchema.safeParse(body)
|
|
|
|
if (!result.success) {
|
|
throw createError({
|
|
statusCode: 400,
|
|
statusMessage: 'Invalid form data',
|
|
data: result.error.flatten()
|
|
})
|
|
}
|
|
|
|
const { name, email, message } = result.data
|
|
|
|
// Check if API key is configured
|
|
if (!config.brevoApiKey || config.brevoApiKey === 'NOT_SET') {
|
|
console.error('BREVO_API_KEY is not configured')
|
|
throw createError({
|
|
statusCode: 500,
|
|
statusMessage: 'Email service is not configured'
|
|
})
|
|
}
|
|
|
|
try {
|
|
// Build HTML email content
|
|
const htmlContent = `
|
|
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<style>
|
|
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; line-height: 1.6; color: #333; }
|
|
.container { max-width: 600px; margin: 0 auto; padding: 20px; }
|
|
.header { background: linear-gradient(135deg, #0ea5e9, #06b6d4); color: white; padding: 24px; border-radius: 12px 12px 0 0; }
|
|
.content { background: #f8fafc; padding: 24px; border: 1px solid #e2e8f0; border-top: none; border-radius: 0 0 12px 12px; }
|
|
.field { margin-bottom: 16px; }
|
|
.label { font-weight: 600; color: #64748b; font-size: 12px; text-transform: uppercase; letter-spacing: 0.5px; }
|
|
.value { margin-top: 4px; padding: 12px; background: white; border-radius: 8px; border: 1px solid #e2e8f0; }
|
|
.message-value { white-space: pre-wrap; }
|
|
.footer { margin-top: 24px; padding-top: 16px; border-top: 1px solid #e2e8f0; font-size: 12px; color: #94a3b8; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="container">
|
|
<div class="header">
|
|
<h1 style="margin: 0; font-size: 24px;">Neue Kontaktanfrage</h1>
|
|
<p style="margin: 8px 0 0; opacity: 0.9;">über GremiumHub Website</p>
|
|
</div>
|
|
<div class="content">
|
|
<div class="field">
|
|
<div class="label">Name</div>
|
|
<div class="value">${escapeHtml(name)}</div>
|
|
</div>
|
|
<div class="field">
|
|
<div class="label">E-Mail</div>
|
|
<div class="value"><a href="mailto:${escapeHtml(email)}">${escapeHtml(email)}</a></div>
|
|
</div>
|
|
<div class="field">
|
|
<div class="label">Nachricht</div>
|
|
<div class="value message-value">${escapeHtml(message)}</div>
|
|
</div>
|
|
<div class="footer">
|
|
Diese Nachricht wurde über das Kontaktformular auf gremiumhub.de gesendet.
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</body>
|
|
</html>
|
|
`.trim()
|
|
|
|
// Send email via Brevo SMTP API
|
|
const response = await $fetch<{ messageId?: string }>('https://api.brevo.com/v3/smtp/email', {
|
|
method: 'POST',
|
|
headers: {
|
|
'api-key': config.brevoApiKey,
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: {
|
|
sender: {
|
|
name: config.brevoSenderName,
|
|
email: config.brevoSenderEmail
|
|
},
|
|
to: [
|
|
{
|
|
email: config.brevoContactEmail,
|
|
name: 'GremiumHub Team'
|
|
}
|
|
],
|
|
replyTo: {
|
|
email,
|
|
name
|
|
},
|
|
subject: `Kontaktanfrage von ${name}`,
|
|
htmlContent
|
|
}
|
|
})
|
|
|
|
return {
|
|
success: true,
|
|
messageId: response.messageId
|
|
}
|
|
} catch (error: unknown) {
|
|
const fetchError = error as { statusCode?: number; data?: { code?: string; message?: string } }
|
|
|
|
console.error('Brevo SMTP API error:', fetchError.data || error)
|
|
|
|
throw createError({
|
|
statusCode: fetchError.statusCode || 500,
|
|
statusMessage: fetchError.data?.message || 'Failed to send message'
|
|
})
|
|
}
|
|
})
|
|
|
|
// Helper function to escape HTML special characters
|
|
function escapeHtml(text: string): string {
|
|
const htmlEntities: Record<string, string> = {
|
|
'&': '&',
|
|
'<': '<',
|
|
'>': '>',
|
|
'"': '"',
|
|
"'": '''
|
|
}
|
|
return text.replace(/[&<>"']/g, (char) => htmlEntities[char] ?? char)
|
|
}
|