diff --git a/prisma/migrations/20260707132629_add_ghl_contact_id/migration.sql b/prisma/migrations/20260707132629_add_ghl_contact_id/migration.sql
new file mode 100644
index 0000000..0ec3afd
--- /dev/null
+++ b/prisma/migrations/20260707132629_add_ghl_contact_id/migration.sql
@@ -0,0 +1,2 @@
+-- AlterTable
+ALTER TABLE "Profile" ADD COLUMN "ghl_contact_id" TEXT;
diff --git a/prisma/schema.prisma b/prisma/schema.prisma
index 523790d..56ca350 100644
--- a/prisma/schema.prisma
+++ b/prisma/schema.prisma
@@ -36,13 +36,14 @@ model User {
// I chose to create a Profile table here because honestly, I didn't want to waste
// a bunch of time trying to figure out how to customize the default Better-Auth model.
model Profile {
- id String @id @default(uuid())
- userId String @unique
- user User @relation(fields: [userId], references: [id], onDelete: Cascade)
- firstName String
- lastName String
- phoneNumber String
- addresses Address[]
+ id String @id @default(uuid())
+ userId String @unique
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
+ firstName String
+ lastName String
+ phoneNumber String
+ ghlContactId String? @map("ghl_contact_id")
+ addresses Address[]
}
model Address {
diff --git a/src/lib/auth.ts b/src/lib/auth.ts
index 1af3605..028e5ca 100644
--- a/src/lib/auth.ts
+++ b/src/lib/auth.ts
@@ -1,24 +1,47 @@
-import { betterAuth } from "better-auth";
-import { prismaAdapter } from "better-auth/adapters/prisma";
-import { prisma } from "$lib/db";
-import { sveltekitCookies } from "better-auth/svelte-kit";
-import { getRequestEvent } from "$app/server";
-
+import { betterAuth } from 'better-auth';
+import { prismaAdapter } from 'better-auth/adapters/prisma';
+import { prisma } from '$lib/db';
+import { sveltekitCookies } from 'better-auth/svelte-kit';
+import { getRequestEvent } from '$app/server';
+import { sendMail } from './server/resend';
export const auth = betterAuth({
- baseURL: process.env.BETTER_AUTH_URL,
- database: prismaAdapter(prisma, {
- provider: "postgresql",
- }),
- emailAndPassword: {
- enabled: true,
- },
- socialProviders: {
- google: {
- prompt: "select_account",
- clientId: process.env.GOOGLE_CLIENT_ID as string,
- clientSecret: process.env.GOOGLE_CLIENT_SECRET as string,
- }
- },
- plugins: [sveltekitCookies(getRequestEvent)],
-})
\ No newline at end of file
+ baseURL: process.env.BETTER_AUTH_URL,
+ database: prismaAdapter(prisma, {
+ provider: 'postgresql'
+ }),
+ emailVerification: {
+ sendVerificationEmail: async ({ user }) => {
+ void sendMail({
+ to: user.email,
+ templateId: 'email-verification-email',
+ data: {
+ target_email_address: user.email,
+ verification_link: 'https://google.com'
+ }
+ });
+ }
+ },
+ emailAndPassword: {
+ enabled: true,
+ requireEmailVerification: true,
+ sendResetPassword: async ({ user, url }) => {
+ void sendMail({
+ to: user.email,
+ templateId: 'password-reset-email',
+ data: {
+ target_email_address: user.email,
+ reset_link: url
+ }
+ });
+ }
+ },
+ socialProviders: {
+ google: {
+ prompt: 'select_account',
+ clientId: process.env.GOOGLE_CLIENT_ID as string,
+ clientSecret: process.env.GOOGLE_CLIENT_SECRET as string
+ }
+ },
+ plugins: [sveltekitCookies(getRequestEvent)]
+});
diff --git a/src/lib/components/ui/topBar/TopBar.svelte b/src/lib/components/ui/topBar/TopBar.svelte
index 5f04ed9..2c9a7cc 100644
--- a/src/lib/components/ui/topBar/TopBar.svelte
+++ b/src/lib/components/ui/topBar/TopBar.svelte
@@ -17,7 +17,8 @@
Dashboard
diff --git a/src/routes/forgot-password/+page.server.ts b/src/routes/forgot-password/+page.server.ts
new file mode 100644
index 0000000..96582c9
--- /dev/null
+++ b/src/routes/forgot-password/+page.server.ts
@@ -0,0 +1,27 @@
+import type { Actions } from './$types';
+import { auth } from '$lib/auth';
+import { fail } from '@sveltejs/kit';
+
+export const actions: Actions = {
+ default: async ({ request }) => {
+ const data = await request.formData();
+ const email = data.get('email') as string;
+
+ if (!email) {
+ return fail(400, { error: 'Email is required.' });
+ }
+
+ try {
+ await auth.api.requestPasswordReset({
+ body: {
+ email,
+ redirectTo: '/reset-password'
+ }
+ });
+ return { success: true };
+ } catch (err) {
+ const message = err instanceof Error ? err.message : 'Failed to send reset email.';
+ return fail(400, { error: message });
+ }
+ }
+};
diff --git a/src/routes/forgot-password/+page.svelte b/src/routes/forgot-password/+page.svelte
new file mode 100644
index 0000000..f1cfc30
--- /dev/null
+++ b/src/routes/forgot-password/+page.svelte
@@ -0,0 +1,42 @@
+
+
+
+
Forgot Password
+
+ {#if form?.success}
+
+ If an account with that email exists, we've sent a password reset link.
+
+ {/if}
+
+ {#if form?.error && !form?.success}
+
+ {form.error}
+
+ {/if}
+
+
+
+
+ Back to Login
+
+
diff --git a/src/routes/login/+page.svelte b/src/routes/login/+page.svelte
index 0c3e5d4..502c8d2 100644
--- a/src/routes/login/+page.svelte
+++ b/src/routes/login/+page.svelte
@@ -40,8 +40,7 @@
Or