From 0d16393b2e4b5ee766bb83b815b040d3be7a7d5b Mon Sep 17 00:00:00 2001 From: WOBBLEFANG THE THIRD Date: Mon, 1 Dec 2025 13:56:04 +0100 Subject: [PATCH] feat: implement user signup functionality with session management --- src/app.d.ts | 6 +++++- src/hooks.server.ts | 24 ++++++++++++++++++++++++ src/lib/server/db/schema.ts | 17 ++++++++++++++--- src/routes/signup/+page.server.ts | 22 ++++++++++++++++++++++ src/routes/signup/+page.svelte | 6 ++++++ 5 files changed, 71 insertions(+), 4 deletions(-) create mode 100644 src/hooks.server.ts create mode 100644 src/routes/signup/+page.server.ts create mode 100644 src/routes/signup/+page.svelte diff --git a/src/app.d.ts b/src/app.d.ts index da08e6d..32d59e3 100644 --- a/src/app.d.ts +++ b/src/app.d.ts @@ -3,7 +3,11 @@ declare global { namespace App { // interface Error {} - // interface Locals {} + interface Locals { + session: { + token: string; + } | null; + } // interface PageData {} // interface PageState {} // interface Platform {} diff --git a/src/hooks.server.ts b/src/hooks.server.ts new file mode 100644 index 0000000..ecf5ffd --- /dev/null +++ b/src/hooks.server.ts @@ -0,0 +1,24 @@ +import { db } from "$lib/server/db"; +import { sessionTable } from "$lib/server/db/schema"; +import type { Handle } from "@sveltejs/kit"; +import { createHash } from "crypto"; +import { and, eq, gt } from "drizzle-orm"; + +export const handle: Handle = async ({ event, resolve }) => { + event.locals.session = null; + + const rawToken = event.cookies.get("session"); + if (rawToken) { + const hashedToken = createHash("sha256").update(rawToken).digest("hex"); + const [sessionRecord] = await db + .select() + .from(sessionTable) + .where(and(eq(sessionTable.token, hashedToken), gt(sessionTable.expires_at, new Date()))); + + if (sessionRecord) { + event.locals.session = { token: sessionRecord.token }; + } + } + + return await resolve(event); +}; diff --git a/src/lib/server/db/schema.ts b/src/lib/server/db/schema.ts index b256205..1d37f1a 100644 --- a/src/lib/server/db/schema.ts +++ b/src/lib/server/db/schema.ts @@ -1,6 +1,17 @@ -import { pgTable, serial, integer } from 'drizzle-orm/pg-core'; +import { pgTable, serial, text, timestamp } from 'drizzle-orm/pg-core'; -export const user = pgTable('user', { +export const userTable = pgTable('user', { id: serial('id').primaryKey(), - age: integer('age') + name: text('name').notNull(), + email: text('email').notNull().unique(), + password: text('password').notNull(), + created_at: timestamp('created_at').defaultNow() +}); + +export const sessionTable = pgTable('session', { + id: serial('id').primaryKey(), + user_id: serial('user_id').notNull().references(() => userTable.id), + token: text('token').notNull().unique(), + expires_at: timestamp('expires_at').notNull(), + created_at: timestamp('created_at').defaultNow() }); diff --git a/src/routes/signup/+page.server.ts b/src/routes/signup/+page.server.ts new file mode 100644 index 0000000..194da35 --- /dev/null +++ b/src/routes/signup/+page.server.ts @@ -0,0 +1,22 @@ +import { db } from '$lib/server/db'; +import { userTable } from '$lib/server/db/schema'; +import type { Actions } from './$types'; + +export const actions = { + default: async ({ request }) => { + const formData = await request.formData(); + const name = formData.get('name'); + const email = formData.get('email'); + const password = formData.get('password'); + + // TODO: Implement data validation. + + const userRecord = await db.insert(userTable).values({ + name: name as string, + email: email as string, + password: password as string + }).returning(); + + // TODO: Handle post-signup logic (e.g., redirect, session creation). + } +} satisfies Actions; \ No newline at end of file diff --git a/src/routes/signup/+page.svelte b/src/routes/signup/+page.svelte new file mode 100644 index 0000000..bb4e4a7 --- /dev/null +++ b/src/routes/signup/+page.svelte @@ -0,0 +1,6 @@ +
+ + + + +
\ No newline at end of file