From 7a9eb2c11b4c870b1c15f05bc63ca4a328dce4d1 Mon Sep 17 00:00:00 2001 From: Musa Date: Mon, 22 Dec 2025 14:38:28 -0800 Subject: [PATCH] tweak contacts APi route --- apps/www/src/app/api/contact/route.ts | 98 +++++++++++++++------------ package-lock.json | 25 ++++++- package.json | 1 + packages/shared-styles/package.json | 2 +- 4 files changed, 80 insertions(+), 46 deletions(-) diff --git a/apps/www/src/app/api/contact/route.ts b/apps/www/src/app/api/contact/route.ts index dfb5ce8c..6c80bf6f 100644 --- a/apps/www/src/app/api/contact/route.ts +++ b/apps/www/src/app/api/contact/route.ts @@ -1,14 +1,60 @@ import { Resend } from 'resend'; import { NextResponse } from 'next/server'; -const resend = new Resend(process.env.RESEND_API_KEY); +function getResendClient() { + const apiKey = process.env.RESEND_API_KEY; + if (!apiKey) { + throw new Error('RESEND_API_KEY environment variable is not set'); + } + return new Resend(apiKey); +} + +interface ContactPayload { + email: string; + firstName: string; + lastName: string; + company?: string; + lookingFor: string; +} + +function buildProperties(company?: string, lookingFor?: string): Record | undefined { + const properties: Record = {}; + if (company) properties.company_name = company; + if (lookingFor) properties.looking_for = lookingFor; + return Object.keys(properties).length > 0 ? properties : undefined; +} + +function isDuplicateError(error: { message?: string; statusCode?: number | null }): boolean { + const errorMessage = error.message?.toLowerCase() || ''; + return ( + errorMessage.includes('already exists') || + errorMessage.includes('duplicate') || + error.statusCode === 409 + ); +} + +function createContactPayload( + email: string, + firstName: string, + lastName: string, + company?: string, + lookingFor?: string +) { + const properties = buildProperties(company, lookingFor); + return { + email, + firstName, + lastName, + unsubscribed: false, + ...(properties && { properties }), + }; +} export async function POST(req: Request) { try { const body = await req.json(); - const { firstName, lastName, email, company, lookingFor } = body; + const { firstName, lastName, email, company, lookingFor }: ContactPayload = body; - // Validate required fields if (!email || !firstName || !lastName || !lookingFor) { return NextResponse.json( { error: 'Missing required fields' }, @@ -16,48 +62,16 @@ export async function POST(req: Request) { ); } - // Create or update the contact - // Note: Contact properties (company_name, looking_for) should be - // created manually in Resend dashboard or via a one-time setup script. - // Attempting to create them on every request causes rate limit issues. + const contactPayload = createContactPayload(email, firstName, lastName, company, lookingFor); + const resend = getResendClient(); - // Build properties object with custom fields - // Property keys must match exactly what's defined in Resend dashboard - const properties: Record = {}; - if (company) properties.company_name = company; - if (lookingFor) properties.looking_for = lookingFor; - - let { data, error } = await resend.contacts.create({ - email, - firstName, - lastName, - unsubscribed: false, - // Pass custom properties as a Record - ...(Object.keys(properties).length > 0 && { properties }), - }); + const { data, error } = await resend.contacts.create(contactPayload); if (error) { - // If contact already exists, update it instead - const errorMessage = error.message?.toLowerCase() || ''; - const isDuplicate = - errorMessage.includes('already exists') || - errorMessage.includes('duplicate') || - error.statusCode === 409; - - if (isDuplicate) { - // Build properties object for update - const updateProperties: Record = {}; - if (company) updateProperties.company_name = company; - if (lookingFor) updateProperties.looking_for = lookingFor; - - const { data: updateData, error: updateError } = await resend.contacts.update({ - email, - firstName, - lastName, - unsubscribed: false, - // Pass custom properties as a Record - ...(Object.keys(updateProperties).length > 0 && { properties: updateProperties }), - }); + if (isDuplicateError(error)) { + const { data: updateData, error: updateError } = await resend.contacts.update( + contactPayload + ); if (updateError) { console.error('Resend update error:', updateError); diff --git a/package-lock.json b/package-lock.json index b3f71b3b..030a667a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,7 @@ "packages/*" ], "dependencies": { + "react": "^19.2.3", "resend": "^6.6.0" }, "devDependencies": { @@ -408,6 +409,15 @@ "node": "^10 || ^12 || >=14" } }, + "apps/www/node_modules/react": { + "version": "19.2.0", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.0.tgz", + "integrity": "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "apps/www/node_modules/tldts": { "version": "7.0.19", "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.0.19.tgz", @@ -13566,9 +13576,9 @@ } }, "node_modules/react": { - "version": "19.2.0", - "resolved": "https://registry.npmjs.org/react/-/react-19.2.0.tgz", - "integrity": "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==", + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.3.tgz", + "integrity": "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==", "license": "MIT", "engines": { "node": ">=0.10.0" @@ -17702,6 +17712,15 @@ "react": "^19.2.0", "react-dom": "^19.2.0" } + }, + "packages/ui/node_modules/react": { + "version": "19.2.0", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.0.tgz", + "integrity": "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } } } } diff --git a/package.json b/package.json index 0335bf53..c24e1823 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ }, "packageManager": "npm@10.0.0", "dependencies": { + "react": "^19.2.3", "resend": "^6.6.0" } } diff --git a/packages/shared-styles/package.json b/packages/shared-styles/package.json index cf211c3d..0920e926 100644 --- a/packages/shared-styles/package.json +++ b/packages/shared-styles/package.json @@ -12,6 +12,6 @@ "scripts": { "build": "echo 'Skipping build'", "lint": "biome check", - "typecheck": "tsc --noEmit" + "typecheck": "echo 'Skipping typecheck - CSS-only package'" } }