From 58842c6f1ec8d1bf79c4664f744ca2e5747292d5 Mon Sep 17 00:00:00 2001
From: Josh Creek <8179928+jcreek@users.noreply.github.com>
Date: Sun, 10 Nov 2024 19:31:23 +0000
Subject: [PATCH 1/6] feat(#79): Create contact page
---
.env.example | 4 +-
src/routes/contact/+page.server.ts | 30 +++++++++++
src/routes/contact/+page.svelte | 87 ++++++++++++++++++++++++++++++
3 files changed, 120 insertions(+), 1 deletion(-)
create mode 100644 src/routes/contact/+page.server.ts
create mode 100644 src/routes/contact/+page.svelte
diff --git a/.env.example b/.env.example
index 79cb895..4ab3dbc 100644
--- a/.env.example
+++ b/.env.example
@@ -11,4 +11,6 @@ VITE_BREVO_API_KEY=""
# Default sender email for system notifications
VITE_BREVO_SENDER_EMAIL="noreply@example.com"
# Display name for the sender email
-VITE_BREVO_SENDER_NAME="No Reply"
\ No newline at end of file
+VITE_BREVO_SENDER_NAME="No Reply"
+# Contact email for the contact page form to send to
+VITE_CONTACT_EMAIL=""
\ No newline at end of file
diff --git a/src/routes/contact/+page.server.ts b/src/routes/contact/+page.server.ts
new file mode 100644
index 0000000..7073ebc
--- /dev/null
+++ b/src/routes/contact/+page.server.ts
@@ -0,0 +1,30 @@
+import type { Actions } from '@sveltejs/kit';
+import { fail } from '@sveltejs/kit';
+import { sendEmail } from '$lib/utils/brevo/email';
+import { VITE_CONTACT_EMAIL } from '$env/static/private';
+import logger from '$lib/utils/logger/logger';
+
+export const actions: Actions = {
+ default: async ({ request }) => {
+ const formData = await request.formData();
+ const name = formData.get('name') as string;
+ const email = formData.get('email') as string;
+ const message = formData.get('message') as string;
+
+ const emailContent = `
+
You have a new contact form submission:
+ Name: ${name}
+ Email: ${email}
+ Message:
+ ${message}
+ `;
+
+ try {
+ await sendEmail('New Contact Form Submission', emailContent, VITE_CONTACT_EMAIL);
+ return { success: true };
+ } catch (error) {
+ logger.error('Failed to send email:', error);
+ return fail(400, { error });
+ }
+ }
+};
diff --git a/src/routes/contact/+page.svelte b/src/routes/contact/+page.svelte
new file mode 100644
index 0000000..271ffec
--- /dev/null
+++ b/src/routes/contact/+page.svelte
@@ -0,0 +1,87 @@
+
+
+{#if submitted}
+
+
+ Thank you for your message, {name}!
+
+
+{:else}
+
+{/if}
From 4137f85190025eb3a51cbd40122a5944e91b6f48 Mon Sep 17 00:00:00 2001
From: Josh Creek <8179928+jcreek@users.noreply.github.com>
Date: Sun, 10 Nov 2024 19:33:32 +0000
Subject: [PATCH 2/6] fix(#79): Add check for result.data
---
src/routes/contact/+page.svelte | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/routes/contact/+page.svelte b/src/routes/contact/+page.svelte
index 271ffec..0c7bdde 100644
--- a/src/routes/contact/+page.svelte
+++ b/src/routes/contact/+page.svelte
@@ -10,7 +10,7 @@
const handleSubmit: SubmitFunction = ({}) => {
return async ({ result, update }) => {
- if (result.data.success) {
+ if (result.data && result.data.success) {
submitted = true;
} else {
error = result.error?.message || 'Form submission failed';
From 6cdc0f0bdec24a90e8ca2bb3f2e6804b47764933 Mon Sep 17 00:00:00 2001
From: Josh Creek <8179928+jcreek@users.noreply.github.com>
Date: Sun, 10 Nov 2024 19:40:59 +0000
Subject: [PATCH 3/6] build(*): Fix broken closing tag preventing build
---
src/routes/account/+page.svelte | 15 +++++++--------
1 file changed, 7 insertions(+), 8 deletions(-)
diff --git a/src/routes/account/+page.svelte b/src/routes/account/+page.svelte
index 88fc742..1c1e4fb 100644
--- a/src/routes/account/+page.svelte
+++ b/src/routes/account/+page.svelte
@@ -34,7 +34,7 @@
} else {
alert('Please sign in to delete your account.');
}
-
+
return async () => {
loading = false;
};
@@ -184,12 +184,11 @@
Provide Feedback
-
-
-
+
+
From 829d8cc031a1fb684ddab089ace71c1a74c48501 Mon Sep 17 00:00:00 2001
From: Josh Creek <8179928+jcreek@users.noreply.github.com>
Date: Sun, 10 Nov 2024 19:45:40 +0000
Subject: [PATCH 4/6] feat(#79): Add rate limiting to contact page form
---
src/lib/utils/rateLimiter.ts | 30 ++++++++++++++++++++++++++++++
src/routes/contact/+page.server.ts | 8 ++++++++
2 files changed, 38 insertions(+)
create mode 100644 src/lib/utils/rateLimiter.ts
diff --git a/src/lib/utils/rateLimiter.ts b/src/lib/utils/rateLimiter.ts
new file mode 100644
index 0000000..1328b24
--- /dev/null
+++ b/src/lib/utils/rateLimiter.ts
@@ -0,0 +1,30 @@
+type RateLimitStore = {
+ [key: string]: { count: number; lastRequest: number };
+};
+
+const rateLimitStore: RateLimitStore = {};
+const RATE_LIMIT_WINDOW = 60 * 5000; // 5 minutes
+const MAX_REQUESTS = 5;
+
+export function isRateLimited(ip: string): boolean {
+ const currentTime = Date.now();
+ const requestInfo = rateLimitStore[ip];
+
+ if (!requestInfo) {
+ rateLimitStore[ip] = { count: 1, lastRequest: currentTime };
+ return false;
+ }
+
+ if (currentTime - requestInfo.lastRequest > RATE_LIMIT_WINDOW) {
+ rateLimitStore[ip] = { count: 1, lastRequest: currentTime };
+ return false;
+ }
+
+ if (requestInfo.count >= MAX_REQUESTS) {
+ return true;
+ }
+
+ rateLimitStore[ip].count += 1;
+ rateLimitStore[ip].lastRequest = currentTime;
+ return false;
+}
diff --git a/src/routes/contact/+page.server.ts b/src/routes/contact/+page.server.ts
index 7073ebc..150a908 100644
--- a/src/routes/contact/+page.server.ts
+++ b/src/routes/contact/+page.server.ts
@@ -3,9 +3,17 @@ import { fail } from '@sveltejs/kit';
import { sendEmail } from '$lib/utils/brevo/email';
import { VITE_CONTACT_EMAIL } from '$env/static/private';
import logger from '$lib/utils/logger/logger';
+import { isRateLimited } from '$lib/utils/rateLimiter';
export const actions: Actions = {
default: async ({ request }) => {
+ const ip =
+ request.headers.get('x-forwarded-for') ?? request.headers.get('remote-addr') ?? 'unknown';
+
+ if (isRateLimited(ip)) {
+ return fail(429, { error: 'Too many requests. Please try again later.' });
+ }
+
const formData = await request.formData();
const name = formData.get('name') as string;
const email = formData.get('email') as string;
From 3b1ffb21a67c7b17fd1423beea21e0f616a1fb27 Mon Sep 17 00:00:00 2001
From: Josh Creek <8179928+jcreek@users.noreply.github.com>
Date: Sun, 10 Nov 2024 19:55:57 +0000
Subject: [PATCH 5/6] feat(#79): Enhance form security and accessibility
---
src/routes/contact/+page.svelte | 18 +++++++++++++++---
1 file changed, 15 insertions(+), 3 deletions(-)
diff --git a/src/routes/contact/+page.svelte b/src/routes/contact/+page.svelte
index 0c7bdde..3ad77a8 100644
--- a/src/routes/contact/+page.svelte
+++ b/src/routes/contact/+page.svelte
@@ -31,7 +31,9 @@
action="?"
use:enhance={handleSubmit}
class="max-w-lg mx-auto p-4 bg-base-200 rounded-lg shadow-md"
+ aria-labelledby="contact-form-title"
>
+ Contact Us
{#if error}