first commit
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
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 },
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user