100 lines
3.2 KiB
TypeScript
100 lines
3.2 KiB
TypeScript
import { NextResponse } from 'next/server';
|
|
import { Resend } from 'resend';
|
|
|
|
export const runtime = 'edge';
|
|
|
|
type ContactPayload = {
|
|
name?: string;
|
|
company?: string;
|
|
service?: string;
|
|
budget?: string;
|
|
message?: string;
|
|
locale?: 'fa' | 'en';
|
|
};
|
|
|
|
const required = ['name', 'service', 'budget', 'message'] as const;
|
|
|
|
function escape(str: string) {
|
|
return str
|
|
.replace(/&/g, '&')
|
|
.replace(/</g, '<')
|
|
.replace(/>/g, '>')
|
|
.replace(/"/g, '"');
|
|
}
|
|
|
|
export async function POST(req: Request) {
|
|
let body: ContactPayload;
|
|
try {
|
|
body = (await req.json()) as ContactPayload;
|
|
} catch {
|
|
return NextResponse.json({ error: 'Invalid JSON' }, { status: 400 });
|
|
}
|
|
|
|
for (const k of required) {
|
|
if (!body[k] || String(body[k]).trim().length < 2) {
|
|
return NextResponse.json(
|
|
{ error: `Missing field: ${k}` },
|
|
{ status: 422 },
|
|
);
|
|
}
|
|
}
|
|
|
|
const apiKey = process.env.RESEND_API_KEY;
|
|
const inbox = process.env.CONTACT_INBOX;
|
|
const from = process.env.CONTACT_FROM;
|
|
|
|
// Graceful no-op for local dev so the form UX can be validated
|
|
// without forcing a Resend key. Production should set these.
|
|
if (!apiKey || !inbox || !from) {
|
|
if (process.env.NODE_ENV === 'production') {
|
|
return NextResponse.json(
|
|
{ error: 'Email service is not configured' },
|
|
{ status: 500 },
|
|
);
|
|
}
|
|
console.info('[contact] received (no Resend key — logging only):', body);
|
|
return NextResponse.json({ ok: true, dev: true });
|
|
}
|
|
|
|
const resend = new Resend(apiKey);
|
|
const subject = `New consultation request — ${body.name}`;
|
|
const html = `
|
|
<div style="font-family: ui-sans-serif, system-ui, sans-serif; line-height: 1.55;">
|
|
<h2 style="margin: 0 0 12px;">New consultation request</h2>
|
|
<table style="border-collapse: collapse;">
|
|
<tr><td style="padding: 4px 12px 4px 0; color:#475569;">Name</td><td>${escape(body.name!)}</td></tr>
|
|
<tr><td style="padding: 4px 12px 4px 0; color:#475569;">Company</td><td>${escape(body.company ?? '')}</td></tr>
|
|
<tr><td style="padding: 4px 12px 4px 0; color:#475569;">Service</td><td>${escape(body.service!)}</td></tr>
|
|
<tr><td style="padding: 4px 12px 4px 0; color:#475569;">Budget</td><td>${escape(body.budget!)}</td></tr>
|
|
<tr><td style="padding: 4px 12px 4px 0; color:#475569;">Locale</td><td>${escape(body.locale ?? '')}</td></tr>
|
|
</table>
|
|
<h3 style="margin: 20px 0 6px;">Message</h3>
|
|
<p style="white-space: pre-wrap; background:#f8fafc; padding:12px; border-radius:8px;">${escape(body.message!)}</p>
|
|
</div>
|
|
`;
|
|
|
|
try {
|
|
const { error } = await resend.emails.send({
|
|
from,
|
|
to: inbox,
|
|
subject,
|
|
html,
|
|
replyTo: body.company ? `${body.name} <${body.company}>` : undefined,
|
|
});
|
|
if (error) {
|
|
console.error('[contact] resend error', error);
|
|
return NextResponse.json(
|
|
{ error: 'Email service rejected the request' },
|
|
{ status: 502 },
|
|
);
|
|
}
|
|
return NextResponse.json({ ok: true });
|
|
} catch (err) {
|
|
console.error('[contact] send failed', err);
|
|
return NextResponse.json(
|
|
{ error: 'Email service unreachable' },
|
|
{ status: 502 },
|
|
);
|
|
}
|
|
}
|