Skip to content

Commit df68867

Browse files
Merge pull request #17 from monashcoding/mac-239-setup-resend-emails
Mac 239 setup resend emails
2 parents 19c7569 + e679c5f commit df68867

6 files changed

Lines changed: 1080 additions & 15 deletions

File tree

.env.example

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ NEXT_PUBLIC_SANITY_PROJECT_ID=your_project_id
33
NEXT_PUBLIC_SANITY_DATASET=production
44
NEXT_PUBLIC_SANITY_API_VERSION=2024-01-18
55

6-
# Sanity CMS Configuration (Sanity CLI: dev, deploy)
7-
SANITY_STUDIO_PROJECT_ID=your_project_id
8-
SANITY_STUDIO_DATASET=production
9-
SANITY_STUDIO_API_VERSION=2024-01-18
6+
# Resend Configuration
7+
# Required: used to send contact form emails via Resend
8+
RESEND_API_KEY=your_resend_api_key
9+

app/api/send/route.ts

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import { EmailTemplate } from '../../../components/EmailTemplate';
2+
import { Resend } from 'resend';
3+
4+
// Validate RESEND_API_KEY is configured
5+
const apiKey = process.env.RESEND_API_KEY;
6+
if (!apiKey || apiKey.trim().length === 0) {
7+
throw new Error(
8+
'RESEND_API_KEY is not configured. Please set the RESEND_API_KEY environment variable.'
9+
);
10+
}
11+
12+
const resend = new Resend(apiKey);
13+
14+
function isNonEmptyString(value: unknown): value is string {
15+
return typeof value === 'string' && value.trim().length > 0;
16+
}
17+
18+
function isValidEmail(value: unknown): value is string {
19+
if (typeof value !== 'string') return false;
20+
const email = value.trim();
21+
// Basic email pattern; not exhaustive but sufficient for simple validation
22+
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
23+
return email.length > 0 && emailRegex.test(email);
24+
}
25+
26+
export async function POST(req: Request) {
27+
try {
28+
// Get the form data from the request
29+
const body = await req.json();
30+
31+
if (!body || typeof body !== 'object') {
32+
return Response.json(
33+
{ error: 'Invalid request body; expected JSON object.' },
34+
{ status: 400 },
35+
);
36+
}
37+
38+
const {
39+
name,
40+
emailAddress,
41+
subject,
42+
message,
43+
} = body as {
44+
name?: unknown;
45+
emailAddress?: unknown;
46+
subject?: unknown;
47+
message?: unknown;
48+
};
49+
50+
if (!isNonEmptyString(name)) {
51+
return Response.json(
52+
{ error: 'Field "name" is required and must be a non-empty string.' },
53+
{ status: 400 },
54+
);
55+
}
56+
57+
if (!isValidEmail(emailAddress)) {
58+
return Response.json(
59+
{ error: 'Field "emailAddress" is required and must be a valid email address.' },
60+
{ status: 400 },
61+
);
62+
}
63+
64+
if (!isNonEmptyString(message)) {
65+
return Response.json(
66+
{ error: 'Field "message" is required and must be a non-empty string.' },
67+
{ status: 400 },
68+
);
69+
}
70+
71+
const normalizedSubject =
72+
typeof subject === 'string' && subject.trim().length > 0
73+
? subject
74+
: 'New Message from Monash Coding Site';
75+
76+
const { data, error } = await resend.emails.send({
77+
from: 'noreply@monashcoding.com',
78+
// to: 'coding@monashclubs.org',
79+
to: 'projects@monashcoding.com',
80+
replyTo: (emailAddress as string).trim(), // User's email will be set as reply-to
81+
subject: normalizedSubject,
82+
react: EmailTemplate({
83+
name: (name as string).trim(),
84+
emailAddress: (emailAddress as string).trim(),
85+
subject: normalizedSubject,
86+
message: (message as string).trim(),
87+
}),
88+
});
89+
90+
if (error) {
91+
console.error('Resend API error:', error);
92+
return Response.json({ error }, { status: 500 });
93+
}
94+
95+
return Response.json(data);
96+
} catch (error) {
97+
console.error('Catch error:', error);
98+
return Response.json({ error: String(error) }, { status: 500 });
99+
}
100+
}

0 commit comments

Comments
 (0)