From ff8b9fca674ca975c100f76ad92ef10ccf404537 Mon Sep 17 00:00:00 2001 From: Bennett Date: Mon, 19 May 2025 17:46:38 +0200 Subject: [PATCH 1/5] add account management page --- src/components/header.tsx | 13 ++++ src/index.tsx | 147 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 160 insertions(+) diff --git a/src/components/header.tsx b/src/components/header.tsx index af3bd37..70fbdf9 100644 --- a/src/components/header.tsx +++ b/src/components/header.tsx @@ -30,6 +30,19 @@ export const Header = ({ )} + {!allowUnauthenticated ? ( +
  • + + Account + +
  • + ) : null} {!allowUnauthenticated ? (
  • { + if (!auth?.value) { + return redirect(`${WEBROOT}/`); + } + const user = await jwt.verify(auth.value); + + if (!user) { + return redirect(`${WEBROOT}/`, 302); + } + + const userData = db + .query("SELECT * FROM users WHERE id = ?") + .as(User) + .get(user.id); + + if (!userData) { + return redirect(`${WEBROOT}/`, 302); + } + + return ( + + <> +
    +
    +
    +
    +
    + + + +
    +
    + +
    +
    +
    +
    + + + ); + }) + .post( + "/account", + async function handler({ body, set, redirect, jwt, cookie: { auth } }) { + if (!auth?.value) { + return redirect(`${WEBROOT}/login`, 302); + } + + const user = await jwt.verify(auth.value); + if (!user) { + return redirect(`${WEBROOT}/login`, 302); + } + const existingUser = db + .query("SELECT * FROM users WHERE id = ?") + .as(User) + .get(user.id); + + if (!existingUser) { + if (auth?.value) { + auth.remove(); + } + return redirect(`${WEBROOT}/login`, 302); + } + + const validPassword = await Bun.password.verify( + body.password, + existingUser.password, + ); + + if (!validPassword) { + set.status = 403; + return { + message: "Invalid credentials.", + }; + } + + const fields = []; + const values = []; + + if (body.email) { + fields.push("email"); + values.push(body.email); + } + if (body.newPassword) { + fields.push("password"); + values.push(await Bun.password.hash(body.newPassword)); + } + + db.query( + `UPDATE users SET ${fields.map((field) => `${field}=?`).join(", ")} WHERE id=?`, + ).run(...values, user.id); + + return redirect(`${WEBROOT}/`, 302); + }, + { + body: t.Object({ + email: t.MaybeEmpty(t.String()), + newPassword: t.MaybeEmpty(t.String()), + password: t.String(), + }), + }, + ) + .get("/", async ({ jwt, redirect, cookie: { auth, jobId } }) => { if (!ALLOW_UNAUTHENTICATED) { if (FIRST_RUN) { From f56a93a1b23c03e036312eb3ac803a35e1a7666a Mon Sep 17 00:00:00 2001 From: Bennett Date: Mon, 19 May 2025 18:19:55 +0200 Subject: [PATCH 2/5] Update src/index.tsx Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> --- src/index.tsx | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/index.tsx b/src/index.tsx index 3d9b47e..002c454 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -599,6 +599,14 @@ const app = new Elysia({ const values = []; if (body.email) { + // Enforce unique email constraint + const existingUser = await db.query( + "SELECT id FROM users WHERE email = ?", + ).get(body.email); + if (existingUser && existingUser.id !== user.id) { + set.status = 409; + return { message: "Email already in use." }; + } fields.push("email"); values.push(body.email); } From b4be479d02e184ec1d56ed5977aad39fb7f24a7c Mon Sep 17 00:00:00 2001 From: Bennett Date: Mon, 19 May 2025 18:20:06 +0200 Subject: [PATCH 3/5] Update src/index.tsx Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> --- src/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/index.tsx b/src/index.tsx index 002c454..5dc8c55 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -531,7 +531,7 @@ const app = new Elysia({ name="newPassword" class="rounded-sm bg-neutral-800 p-3" placeholder="Password" - autocomplete="current-password" + autocomplete="new-password" />