From 172504fa66192cca9fe2e717c494c3bb9540abf7 Mon Sep 17 00:00:00 2001 From: Sean Morley Date: Sat, 20 Apr 2024 16:59:03 +0000 Subject: [PATCH 1/4] Add version detection endpoint --- package.json | 2 +- src/routes/api/version/+server.ts | 27 +++++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 src/routes/api/version/+server.ts diff --git a/package.json b/package.json index c0ff76f..847b2c2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "adventurelog", - "version": "0.1.0", + "version": "0.1.6", "description": "Embark, Explore, Remember. 🌍", "private": true, "scripts": { diff --git a/src/routes/api/version/+server.ts b/src/routes/api/version/+server.ts new file mode 100644 index 0000000..814e1f8 --- /dev/null +++ b/src/routes/api/version/+server.ts @@ -0,0 +1,27 @@ +import type { RequestEvent } from "@sveltejs/kit"; +import { readFileSync } from "fs"; +import { join, dirname } from "path"; +import { fileURLToPath } from "url"; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); + +const packageJsonPath = join(__dirname, "..", "..", "..", "..", "package.json"); +const json = readFileSync(packageJsonPath, "utf8"); +const pkg = JSON.parse(json); + +const version = pkg.version; + +/** + * Handles the GET request for the version API endpoint. + * @param event - The request event object. + * @returns A Promise that resolves to a Response object. + */ +export async function GET(event: RequestEvent): Promise { + return new Response(JSON.stringify({ version: version }), { + status: 200, + headers: { + "Content-Type": "application/json", + }, + }); +} From ef4249142110348c13d87da96071ab8ff163d1d0 Mon Sep 17 00:00:00 2001 From: Sean Morley Date: Sat, 20 Apr 2024 17:32:46 +0000 Subject: [PATCH 2/4] Refactor version API endpoint to use package.json for version detection --- src/routes/api/version/+server.ts | 27 --------------------------- 1 file changed, 27 deletions(-) delete mode 100644 src/routes/api/version/+server.ts diff --git a/src/routes/api/version/+server.ts b/src/routes/api/version/+server.ts deleted file mode 100644 index 814e1f8..0000000 --- a/src/routes/api/version/+server.ts +++ /dev/null @@ -1,27 +0,0 @@ -import type { RequestEvent } from "@sveltejs/kit"; -import { readFileSync } from "fs"; -import { join, dirname } from "path"; -import { fileURLToPath } from "url"; - -const __filename = fileURLToPath(import.meta.url); -const __dirname = dirname(__filename); - -const packageJsonPath = join(__dirname, "..", "..", "..", "..", "package.json"); -const json = readFileSync(packageJsonPath, "utf8"); -const pkg = JSON.parse(json); - -const version = pkg.version; - -/** - * Handles the GET request for the version API endpoint. - * @param event - The request event object. - * @returns A Promise that resolves to a Response object. - */ -export async function GET(event: RequestEvent): Promise { - return new Response(JSON.stringify({ version: version }), { - status: 200, - headers: { - "Content-Type": "application/json", - }, - }); -} From a186d514af2e3bd22fcea591f0c31abb900f5731 Mon Sep 17 00:00:00 2001 From: Sean Morley Date: Sun, 21 Apr 2024 16:16:29 +0000 Subject: [PATCH 3/4] Refactor admin settings page and add user signup functionality --- src/routes/settings/admin/+page.server.ts | 12 ++++ src/routes/settings/admin/+page.svelte | 80 +++++++++++++++++++++++ src/routes/signup/+page.server.ts | 46 ++++++++++--- 3 files changed, 129 insertions(+), 9 deletions(-) create mode 100644 src/routes/settings/admin/+page.server.ts create mode 100644 src/routes/settings/admin/+page.svelte diff --git a/src/routes/settings/admin/+page.server.ts b/src/routes/settings/admin/+page.server.ts new file mode 100644 index 0000000..3837865 --- /dev/null +++ b/src/routes/settings/admin/+page.server.ts @@ -0,0 +1,12 @@ +import { error, redirect, type Actions } from "@sveltejs/kit"; +import type { PageServerLoad } from "./$types"; + +export const load: PageServerLoad = async (event) => { + if (!event.locals.user) { + return redirect(302, "/login"); + } else { + if (event.locals.user.role !== "admin") { + return redirect(302, "/settings"); + } + } +}; diff --git a/src/routes/settings/admin/+page.svelte b/src/routes/settings/admin/+page.svelte new file mode 100644 index 0000000..fabd082 --- /dev/null +++ b/src/routes/settings/admin/+page.svelte @@ -0,0 +1,80 @@ + + +

Admin Settings

+ +

Add User

+
+
+ +
+ +
+ +
+ +
+ +
+ +
+
+ +{#if errors.message} +
+ {errors.message} +
+{/if} diff --git a/src/routes/signup/+page.server.ts b/src/routes/signup/+page.server.ts index 691db63..298c418 100644 --- a/src/routes/signup/+page.server.ts +++ b/src/routes/signup/+page.server.ts @@ -1,6 +1,6 @@ // routes/signup/+page.server.ts import { lucia } from "$lib/server/auth"; -import { fail, redirect } from "@sveltejs/kit"; +import { error, fail, redirect } from "@sveltejs/kit"; import { generateId } from "lucia"; import { Argon2id } from "oslo/password"; import { db } from "$lib/db/db.server"; @@ -17,6 +17,7 @@ export const actions: Actions = { const password = formData.get("password"); const firstName = formData.get("first_name"); const lastName = formData.get("last_name"); + let role: string = ""; // username must be between 4 ~ 31 characters, and only consists of lowercase letters, 0-9, -, and _ // keep in mind some database (e.g. mysql) are case insensitive @@ -27,6 +28,15 @@ export const actions: Actions = { }); } + if (!event.locals.user) { + role = "user"; + } + + if (event.locals.user && event.locals.user.role === "admin") { + const isAdmin = formData.get("role") === "on"; + role = isAdmin ? "admin" : "user"; + } + if ( typeof username !== "string" || username.length < 3 || @@ -91,18 +101,36 @@ export const actions: Actions = { last_name: lastName, hashed_password: hashedPassword, signup_date: new Date(), - role: "user", + role: role, last_login: new Date(), } as DatabaseUser) .execute(); - const session = await lucia.createSession(userId, {}); - const sessionCookie = lucia.createSessionCookie(session.id); - event.cookies.set(sessionCookie.name, sessionCookie.value, { - path: ".", - ...sessionCookie.attributes, - }); + if (!event.locals.user) { + const session = await lucia.createSession(userId, {}); + const sessionCookie = lucia.createSessionCookie(session.id); + event.cookies.set(sessionCookie.name, sessionCookie.value, { + path: ".", + ...sessionCookie.attributes, + }); - redirect(302, "/"); + redirect(302, "/"); + } else { + if (event.locals.user && event.locals.user.role !== "admin") { + return error(403, { + message: "You are not authorized to add users", + }); + } + + return { + status: 200, + headers: { + "content-type": "application/json", + }, + body: JSON.stringify({ + message: "User created", + }), + }; + } }, }; From 29e9b308ba23eef40b46e564dfca92f2c6f5df08 Mon Sep 17 00:00:00 2001 From: Sean Morley Date: Sun, 21 Apr 2024 16:31:55 +0000 Subject: [PATCH 4/4] Refactor admin settings page, clear all sessions, and add user signup functionality --- src/routes/+layout.svelte | 1 - src/routes/settings/admin/+page.server.ts | 26 ++++++++++++++++++++++- src/routes/settings/admin/+page.svelte | 24 ++++++++++++++++++++- src/routes/signup/+page.server.ts | 19 +++++++++-------- 4 files changed, 58 insertions(+), 12 deletions(-) diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index 67b0334..904454a 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -19,7 +19,6 @@ let isServerSetup = data.isServerSetup; onMount(() => { - console.log("isServerSetup", isServerSetup); if (!isServerSetup && $page.url.pathname !== "/setup") { goto("/setup"); } diff --git a/src/routes/settings/admin/+page.server.ts b/src/routes/settings/admin/+page.server.ts index 3837865..888ea6e 100644 --- a/src/routes/settings/admin/+page.server.ts +++ b/src/routes/settings/admin/+page.server.ts @@ -1,5 +1,7 @@ -import { error, redirect, type Actions } from "@sveltejs/kit"; +import { error, redirect, type Actions, type Handle } from "@sveltejs/kit"; import type { PageServerLoad } from "./$types"; +import { db } from "$lib/db/db.server"; +import { sessionTable } from "$lib/db/schema"; export const load: PageServerLoad = async (event) => { if (!event.locals.user) { @@ -10,3 +12,25 @@ export const load: PageServerLoad = async (event) => { } } }; + +export const actions: Actions = { + clearAllSessions: async (event) => { + if (event.locals.user && event.locals.user.role !== "admin") { + return error(403, { + message: "You are not authorized to perform this action", + }); + } else { + console.log("ALL SESSIONS CLEARED"); + await db.delete(sessionTable).execute(); + return { + status: 200, + headers: { + "content-type": "application/json", + }, + body: JSON.stringify({ + message: "Cleared all sessions", + }), + }; + } + }, +}; diff --git a/src/routes/settings/admin/+page.svelte b/src/routes/settings/admin/+page.svelte index fabd082..d79147d 100644 --- a/src/routes/settings/admin/+page.svelte +++ b/src/routes/settings/admin/+page.svelte @@ -4,6 +4,10 @@ import { type SubmitFunction } from "@sveltejs/kit"; let errors: { message?: string } = {}; let message: { message?: string } = {}; + let username: string = ""; + let first_name: string = ""; + let last_name: string = ""; + let password: string = ""; const addUser: SubmitFunction = async ({ formData, action, cancel }) => { const response = await fetch(action, { method: "POST", @@ -13,8 +17,11 @@ if (response.ok) { console.log("User Added Successfully!"); errors = {}; + username = ""; + first_name = ""; + last_name = ""; + password = ""; cancel(); - window.location.reload(); return; } @@ -41,18 +48,21 @@


@@ -60,6 +70,7 @@ type="password" name="password" id="password" + bind:value={password} class="block mb-2 input input-bordered w-full max-w-xs" />
@@ -78,3 +89,14 @@ {errors.message} {/if} + +

Session Managment

+
+
+ +
+
diff --git a/src/routes/signup/+page.server.ts b/src/routes/signup/+page.server.ts index 298c418..e287775 100644 --- a/src/routes/signup/+page.server.ts +++ b/src/routes/signup/+page.server.ts @@ -23,7 +23,7 @@ export const actions: Actions = { // check all to make sure all fields are provided if (!username || !password || !firstName || !lastName) { - return fail(400, { + return error(400, { message: "All fields are required", }); } @@ -43,7 +43,7 @@ export const actions: Actions = { username.length > 31 || !/^[a-z0-9_-]+$/.test(username) ) { - return fail(400, { + return error(400, { message: "Invalid username", }); } @@ -52,7 +52,7 @@ export const actions: Actions = { password.length < 6 || password.length > 255 ) { - return fail(400, { + return error(400, { message: "Invalid password", }); } @@ -62,7 +62,7 @@ export const actions: Actions = { firstName.length < 1 || firstName.length > 255 ) { - return fail(400, { + return error(400, { message: "Invalid first name", }); } @@ -72,14 +72,11 @@ export const actions: Actions = { lastName.length < 1 || lastName.length > 255 ) { - return fail(400, { + return error(400, { message: "Invalid last name", }); } - const userId = generateId(15); - const hashedPassword = await new Argon2id().hash(password); - const usernameTaken = await db .select() .from(userTable) @@ -88,10 +85,14 @@ export const actions: Actions = { .then((results) => results[0] as unknown as DatabaseUser | undefined); if (usernameTaken) { - return fail(400, { + return error(400, { message: "Username already taken", }); } + + const userId = generateId(15); + const hashedPassword = await new Argon2id().hash(password); + await db .insert(userTable) .values({