Skip to content

Commit a777ab0

Browse files
committed
Configure contact form with Turnstile and MailLayer templates
- Add Turnstile site key - Update worker to use MailLayer templates with variables - Variables: [name], [email], [project], [topic], [message] - Update worker URL
1 parent 0a0bbec commit a777ab0

3 files changed

Lines changed: 35 additions & 89 deletions

File tree

src/pages/contact.astro

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ const topics = [
7676
</div>
7777

7878
<!-- Cloudflare Turnstile -->
79-
<div class="cf-turnstile" data-sitekey="YOUR_TURNSTILE_SITE_KEY" data-theme="auto"></div>
79+
<div class="cf-turnstile" data-sitekey="0x4AAAAAACJRfknj9jWePSij" data-theme="auto"></div>
8080

8181
<button type="submit" class="btn btn--primary submit-btn">
8282
<span class="btn-text">Send Message</span>
@@ -103,7 +103,7 @@ const topics = [
103103
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>
104104

105105
<script is:inline>
106-
const WORKER_URL = 'https://contact.simplebytes.com'; // Update with your worker URL
106+
const WORKER_URL = 'https://simplebytes-contact.simplebytes-com.workers.dev';
107107

108108
const form = document.getElementById('contact-form');
109109
const successMessage = document.getElementById('form-success');

worker/contact-form.js

Lines changed: 29 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,22 @@
44
* Handles form submissions with:
55
* - Cloudflare Turnstile verification
66
* - Honeypot spam detection
7-
* - Email sending via MailLayer
7+
* - Email sending via MailLayer templates
88
*
99
* Environment variables required:
1010
* - TURNSTILE_SECRET_KEY: Cloudflare Turnstile secret key
1111
* - MAILLAYER_API_KEY: MailLayer API key
12+
* - CONFIRMATION_TEMPLATE_ID: MailLayer template ID for user confirmation
13+
* - ADMIN_TEMPLATE_ID: MailLayer template ID for admin notification
1214
* - ADMIN_EMAIL: Email to receive submissions (default: hello@simplebytes.com)
1315
* - FROM_EMAIL: Email to send from (e.g., noreply@simplebytes.com)
16+
*
17+
* Template variables available:
18+
* - [name]: Sender's name
19+
* - [email]: Sender's email
20+
* - [project]: Selected project name
21+
* - [topic]: Selected topic
22+
* - [message]: Message content
1423
*/
1524

1625
const CORS_HEADERS = {
@@ -82,16 +91,28 @@ export default {
8291
// Send confirmation email to submitter
8392
await sendEmail(env, {
8493
to: email,
85-
subject: `We received your message - Simple Bytes`,
86-
html: getConfirmationEmailHtml(name, projectLabel, topicLabel, message),
94+
templateId: env.CONFIRMATION_TEMPLATE_ID,
95+
variables: {
96+
name,
97+
email,
98+
project: projectLabel,
99+
topic: topicLabel,
100+
message,
101+
},
87102
});
88103

89104
// Send notification to admin
90105
await sendEmail(env, {
91106
to: env.ADMIN_EMAIL || 'hello@simplebytes.com',
92107
replyTo: email,
93-
subject: `[${projectLabel}] ${topicLabel} from ${name}`,
94-
html: getAdminEmailHtml(name, email, projectLabel, topicLabel, message),
108+
templateId: env.ADMIN_TEMPLATE_ID,
109+
variables: {
110+
name,
111+
email,
112+
project: projectLabel,
113+
topic: topicLabel,
114+
message,
115+
},
95116
});
96117

97118
return jsonResponse({ success: true });
@@ -133,7 +154,7 @@ async function verifyTurnstile(token, secretKey) {
133154
return result.success === true;
134155
}
135156

136-
async function sendEmail(env, { to, replyTo, subject, html }) {
157+
async function sendEmail(env, { to, replyTo, templateId, variables }) {
137158
const response = await fetch('https://api.maillayer.com/v1/send', {
138159
method: 'POST',
139160
headers: {
@@ -144,8 +165,8 @@ async function sendEmail(env, { to, replyTo, subject, html }) {
144165
from: env.FROM_EMAIL || 'noreply@simplebytes.com',
145166
to,
146167
reply_to: replyTo,
147-
subject,
148-
html,
168+
template_id: templateId,
169+
variables,
149170
}),
150171
});
151172

@@ -156,82 +177,3 @@ async function sendEmail(env, { to, replyTo, subject, html }) {
156177

157178
return response.json();
158179
}
159-
160-
function getConfirmationEmailHtml(name, project, topic, message) {
161-
return `
162-
<!DOCTYPE html>
163-
<html>
164-
<head>
165-
<meta charset="utf-8">
166-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
167-
</head>
168-
<body style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; line-height: 1.6; color: #171717; max-width: 600px; margin: 0 auto; padding: 20px;">
169-
<div style="border-bottom: 1px solid #e5e5e5; padding-bottom: 20px; margin-bottom: 20px;">
170-
<h1 style="font-size: 24px; font-weight: 600; margin: 0;">Simple Bytes</h1>
171-
</div>
172-
173-
<p>Hi ${escapeHtml(name)},</p>
174-
175-
<p>Thank you for reaching out! We've received your message and will get back to you as soon as possible.</p>
176-
177-
<div style="background: #f5f5f5; border-radius: 8px; padding: 20px; margin: 20px 0;">
178-
<p style="margin: 0 0 10px 0;"><strong>Project:</strong> ${escapeHtml(project)}</p>
179-
<p style="margin: 0 0 10px 0;"><strong>Topic:</strong> ${escapeHtml(topic)}</p>
180-
<p style="margin: 0 0 10px 0;"><strong>Your message:</strong></p>
181-
<p style="margin: 0; white-space: pre-wrap;">${escapeHtml(message)}</p>
182-
</div>
183-
184-
<p>Best regards,<br>The Simple Bytes Team</p>
185-
186-
<div style="border-top: 1px solid #e5e5e5; padding-top: 20px; margin-top: 20px; font-size: 12px; color: #737373;">
187-
<p style="margin: 0;">This is an automated confirmation. Please do not reply to this email.</p>
188-
<p style="margin: 10px 0 0 0;"><a href="https://simplebytes.com" style="color: #525252;">simplebytes.com</a></p>
189-
</div>
190-
</body>
191-
</html>
192-
`;
193-
}
194-
195-
function getAdminEmailHtml(name, email, project, topic, message) {
196-
return `
197-
<!DOCTYPE html>
198-
<html>
199-
<head>
200-
<meta charset="utf-8">
201-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
202-
</head>
203-
<body style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; line-height: 1.6; color: #171717; max-width: 600px; margin: 0 auto; padding: 20px;">
204-
<div style="border-bottom: 1px solid #e5e5e5; padding-bottom: 20px; margin-bottom: 20px;">
205-
<h1 style="font-size: 24px; font-weight: 600; margin: 0;">New Contact Form Submission</h1>
206-
</div>
207-
208-
<div style="background: #f5f5f5; border-radius: 8px; padding: 20px; margin-bottom: 20px;">
209-
<p style="margin: 0 0 10px 0;"><strong>Name:</strong> ${escapeHtml(name)}</p>
210-
<p style="margin: 0 0 10px 0;"><strong>Email:</strong> <a href="mailto:${escapeHtml(email)}">${escapeHtml(email)}</a></p>
211-
<p style="margin: 0 0 10px 0;"><strong>Project:</strong> ${escapeHtml(project)}</p>
212-
<p style="margin: 0;"><strong>Topic:</strong> ${escapeHtml(topic)}</p>
213-
</div>
214-
215-
<div style="background: #fafafa; border: 1px solid #e5e5e5; border-radius: 8px; padding: 20px;">
216-
<p style="margin: 0 0 10px 0;"><strong>Message:</strong></p>
217-
<p style="margin: 0; white-space: pre-wrap;">${escapeHtml(message)}</p>
218-
</div>
219-
220-
<p style="margin-top: 20px; font-size: 12px; color: #737373;">
221-
Reply directly to this email to respond to ${escapeHtml(name)}.
222-
</p>
223-
</body>
224-
</html>
225-
`;
226-
}
227-
228-
function escapeHtml(text) {
229-
const map = {
230-
'&': '&amp;',
231-
'<': '&lt;',
232-
'>': '&gt;',
233-
'"': '&quot;',
234-
"'": '&#039;',
235-
};
236-
return String(text).replace(/[&<>"']/g, m => map[m]);
237-
}

worker/wrangler.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
name = "simplebytes-contact"
22
main = "contact-form.js"
33
compatibility_date = "2024-01-01"
4+
account_id = "4963e2d7410e0650e45f31d098a84880"
45

56
# Custom domain - configure in Cloudflare dashboard
67
# routes = [{ pattern = "contact.simplebytes.com", custom_domain = true }]
78

89
[vars]
910
ADMIN_EMAIL = "hello@simplebytes.com"
1011
FROM_EMAIL = "noreply@simplebytes.com"
12+
# Add your MailLayer template IDs here:
13+
CONFIRMATION_TEMPLATE_ID = ""
14+
ADMIN_TEMPLATE_ID = ""
1115

1216
# Secrets (add via `wrangler secret put`):
1317
# - TURNSTILE_SECRET_KEY

0 commit comments

Comments
 (0)