import type { FastifyInstance } from 'fastify'; import { z } from 'zod'; import { getDb, contactMessages } from '@lawdesk/db'; import { sendEmail, contactAckEmail, contactNotifyEmail } from '../lib/email'; import { env } from '../env'; const contactBody = z.object({ fullName: z.string().min(1).max(120).trim(), email: z.string().email().max(254).toLowerCase().trim(), message: z.string().min(1).max(5000).trim(), }); export async function contactRoutes(app: FastifyInstance) { app.post( '/api/contact', { config: { rateLimit: { max: 5, timeWindow: '10 minutes' } } }, async (req, reply) => { const parsed = contactBody.safeParse(req.body); if (!parsed.success) return reply.code(400).send({ error: 'invalid_input' }); const body = parsed.data; await getDb().insert(contactMessages).values({ fullName: body.fullName, email: body.email, message: body.message, ip: req.ip ?? null, }); const tpl = contactAckEmail(body.fullName); sendEmail({ to: body.email, ...tpl }).catch((err) => app.log.warn({ err }, 'contact ack email failed'), ); // Notify the team — reply-to points at the submitter so answering is one click. const notify = contactNotifyEmail({ fromName: body.fullName, fromEmail: body.email, message: body.message, ip: req.ip ?? null, }); for (const admin of env.superadminEmails) { sendEmail({ to: admin, ...notify, replyTo: body.email }).catch((err) => app.log.warn({ err }, 'contact notify email failed'), ); } return reply.code(201).send({ ok: true }); }, ); }