diff --git a/src/routes/(public)/login/+page.server.ts b/src/routes/(public)/login/+page.server.ts deleted file mode 100644 index 8dcf6ce..0000000 --- a/src/routes/(public)/login/+page.server.ts +++ /dev/null @@ -1,138 +0,0 @@ -import type { PageServerLoad, Actions } from "./$types.js"; -import { fail } from "@sveltejs/kit"; -import { superValidate } from "sveltekit-superforms"; -import { zod } from "sveltekit-superforms/adapters"; -import { formSchema } from "./schema"; - -export const load: PageServerLoad = async () => { - return { - form: await superValidate(zod(formSchema)), - }; -}; - -export const actions: Actions = { - default: async (event) => { - const form = await superValidate(event, zod(formSchema)); - if (!form.valid) { - return fail(400, { - form, - }); - } - return { - form, - }; - }, -}; - -// import { hash, verify } from '@node-rs/argon2'; -// import { encodeBase32LowerCase } from '@oslojs/encoding'; -// import { fail, redirect } from '@sveltejs/kit'; -// import { eq } from 'drizzle-orm'; -// import * as auth from '$lib/server/auth'; -// import { db } from '$lib/server/db'; -// import * as table from '$lib/server/db/schema'; -// import type { Actions, PageServerLoad } from './$types'; - -// export const load: PageServerLoad = async (event) => { -// if (event.locals.user) { -// return redirect(302, '/demo/lucia'); -// } -// return {}; -// }; - -// export const actions: Actions = { -// login: async (event) => { -// const formData = await event.request.formData(); -// const username = formData.get('username'); -// const password = formData.get('password'); - -// if (!validateUsername(username)) { -// return fail(400, { message: 'Invalid username (min 3, max 31 characters, alphanumeric only)' }); -// } -// if (!validatePassword(password)) { -// return fail(400, { message: 'Invalid password (min 6, max 255 characters)' }); -// } - -// const results = await db -// .select() -// .from(table.user) -// .where(eq(table.user.username, username)); - -// const existingUser = results.at(0); -// if (!existingUser) { -// return fail(400, { message: 'Incorrect username or password' }); -// } - -// const validPassword = await verify(existingUser.passwordHash, password, { -// memoryCost: 19456, -// timeCost: 2, -// outputLen: 32, -// parallelism: 1, -// }); -// if (!validPassword) { -// return fail(400, { message: 'Incorrect username or password' }); -// } - -// const sessionToken = auth.generateSessionToken(); -// const session = await auth.createSession(sessionToken, existingUser.id); -// auth.setSessionTokenCookie(event, sessionToken, session.expiresAt); - -// return redirect(302, '/demo/lucia'); -// }, -// register: async (event) => { -// const formData = await event.request.formData(); -// const username = formData.get('username'); -// const password = formData.get('password'); - -// if (!validateUsername(username)) { -// return fail(400, { message: 'Invalid username' }); -// } -// if (!validatePassword(password)) { -// return fail(400, { message: 'Invalid password' }); -// } - -// const userId = generateUserId(); -// const passwordHash = await hash(password, { -// // recommended minimum parameters -// memoryCost: 19456, -// timeCost: 2, -// outputLen: 32, -// parallelism: 1, -// }); - -// try { -// await db.insert(table.user).values({ id: userId, username, passwordHash }); - -// const sessionToken = auth.generateSessionToken(); -// const session = await auth.createSession(sessionToken, userId); -// auth.setSessionTokenCookie(event, sessionToken, session.expiresAt); -// } catch (e) { -// return fail(500, { message: 'An error has occurred' }); -// } -// return redirect(302, '/demo/lucia'); -// }, -// }; - -// function generateUserId() { -// // ID with 120 bits of entropy, or about the same as UUID v4. -// const bytes = crypto.getRandomValues(new Uint8Array(15)); -// const id = encodeBase32LowerCase(bytes); -// return id; -// } - -// function validateUsername(username: unknown): username is string { -// return ( -// typeof username === 'string' && -// username.length >= 3 && -// username.length <= 31 && -// /^[a-z0-9_-]+$/.test(username) -// ); -// } - -// function validatePassword(password: unknown): password is string { -// return ( -// typeof password === 'string' && -// password.length >= 6 && -// password.length <= 255 -// ); -// } diff --git a/src/routes/(public)/login/login-form.svelte b/src/routes/(public)/login/login-form.svelte deleted file mode 100644 index 0d0b147..0000000 --- a/src/routes/(public)/login/login-form.svelte +++ /dev/null @@ -1,77 +0,0 @@ - - -
- - - LOGIN - - -
-
-
-
- - - {#snippet children({ props })} - Email - - {/snippet} - - - -
-
- - - {#snippet children({ props })} - password - - {/snippet} - - - -
-
- - -
- 로그인 -
-
- 이메일 인증을 통한 - 회원가입 -
-
-
-
-
-
diff --git a/src/routes/(public)/login/schema.ts b/src/routes/(public)/login/schema.ts deleted file mode 100644 index aa1e074..0000000 --- a/src/routes/(public)/login/schema.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { z } from "zod"; - -export const formSchema = z.object({ - email: z.string().email(), - password: z.string().min(8), -}); - -export type FormSchema = typeof formSchema; \ No newline at end of file diff --git a/src/routes/(public)/login/+layout.svelte b/src/routes/login/+layout.svelte similarity index 100% rename from src/routes/(public)/login/+layout.svelte rename to src/routes/login/+layout.svelte diff --git a/src/routes/login/+page.server.ts b/src/routes/login/+page.server.ts new file mode 100644 index 0000000..772429e --- /dev/null +++ b/src/routes/login/+page.server.ts @@ -0,0 +1,67 @@ +import {hash, verify} from '@node-rs/argon2'; +import {encodeBase32LowerCase} from '@oslojs/encoding'; +import {redirect} from '@sveltejs/kit'; +import {eq} from 'drizzle-orm'; +import * as auth from '$lib/server/auth'; +import {db} from '$lib/server/db'; +import * as table from '$lib/server/db/schema'; +import type {Actions, PageServerLoad} from './$types'; +import { setError, superValidate , fail} from 'sveltekit-superforms'; +import {zod} from "sveltekit-superforms/adapters"; +import {formSchema} from "./schema"; + + + +export const load: PageServerLoad = async (event) => { + if (event.locals.user) { + return redirect(302, '/demo/lucia'); + } + return { + form: await superValidate(zod(formSchema)), + }; +}; + +export const actions: Actions = { + login: async (event) => { + const form = await superValidate(event, zod(formSchema)); + if (!form.valid) { + return fail(400, { + form, + }); + } + + const userId = form.data.userId; + const password = form.data.password; + + const results = await db + .select() + .from(table.user) + .where(eq(table.user.username, userId)); + + const existingUser = results.at(0); + if (!existingUser) { + return setError(form, 'userId', '존재하지 않는 아이디 입니다.'); + } + + const validPassword = await verify(existingUser.passwordHash, password, { + memoryCost: 19456, + timeCost: 2, + outputLen: 32, + parallelism: 1, + }); + if (!validPassword) { + return setError(form, 'password', '비밀번호가 일치하지 않습니다.'); + } + + const sessionToken = auth.generateSessionToken(); + const session = await auth.createSession(sessionToken, existingUser.id); + auth.setSessionTokenCookie(event, sessionToken, session.expiresAt); + + return redirect(302, '/demo/lucia'); + + }, +}; + + + + diff --git a/src/routes/(public)/login/+page.svelte b/src/routes/login/+page.svelte similarity index 81% rename from src/routes/(public)/login/+page.svelte rename to src/routes/login/+page.svelte index 63ad961..cf0bbd7 100644 --- a/src/routes/(public)/login/+page.svelte +++ b/src/routes/login/+page.svelte @@ -1,5 +1,5 @@ diff --git a/src/routes/login/login-form.svelte b/src/routes/login/login-form.svelte new file mode 100644 index 0000000..1ddfdae --- /dev/null +++ b/src/routes/login/login-form.svelte @@ -0,0 +1,81 @@ + + +
+ + + LOGIN + + +
+
+
+
+ + + {#snippet children({props})} + UserId + + {/snippet} + + + +
+
+ + + {#snippet children({props})} + password + + {/snippet} + + + +
+
+ + +
+ 로그인 +
+
+ 이메일 인증을 통한 + 회원가입 +
+
+
+
+
+ {#if dev} + + {/if} +
diff --git a/src/routes/login/schema.ts b/src/routes/login/schema.ts new file mode 100644 index 0000000..56432a5 --- /dev/null +++ b/src/routes/login/schema.ts @@ -0,0 +1,22 @@ +import { z } from "zod"; + +export const formSchema = z.object({ + // User ID (formerly 'user') + userId: z.string() + .min(3, "최소 3자 이상이어야 합니다.") // A common minimum for IDs + .max(50, "최대 50자 이하이어야 합니다.") // Reasonable maximum length + .regex(/^[a-zA-Z0-9_.-]+$/, "영문, 숫자, '_', '.', '-'만 포함할 수 있습니다.") // Restrict characters + .trim(), // Remove leading/trailing whitespace + + // Password + password: z.string() + .min(8, "최소 8자 이상이어야 합니다.") + .max(100, "최대 100자 이하이어야 합니다.") // Reasonable maximum length + .regex(/[a-z]/, "최소 하나의 소문자를 포함해야 합니다.") + .regex(/[A-Z]/, "최소 하나의 대문자를 포함해야 합니다.") + .regex(/[0-9]/, "최소 하나의 숫자를 포함해야 합니다.") + .regex(/[^a-zA-Z0-9]/, "최소 하나의 특수문자를 포함해야 합니다.") // For common special characters + .trim(), +}); + +export type FormSchema = typeof formSchema; \ No newline at end of file diff --git a/src/routes/sendmailtest/+page.server.js b/src/routes/sendmailtest/+page.server.ts similarity index 98% rename from src/routes/sendmailtest/+page.server.js rename to src/routes/sendmailtest/+page.server.ts index a50c564..3dde172 100644 --- a/src/routes/sendmailtest/+page.server.js +++ b/src/routes/sendmailtest/+page.server.ts @@ -1,5 +1,5 @@ import { fail } from '@sveltejs/kit'; -import nodemailer from 'nodemailer'; +const nodemailer = require("nodemailer"); import { BREVO_SMTP_HOST, BREVO_SMTP_PORT,