From 104e142e43f49175675f6493c77fc2624697fc08 Mon Sep 17 00:00:00 2001 From: 409 <409dev@protonmail.com> Date: Mon, 9 Jun 2025 21:09:21 +0200 Subject: [PATCH] register / login --- bun.lock | 21 ++++-- components.json | 4 +- package.json | 7 +- src/lib/Navbar.svelte | 20 ++++++ src/lib/auth.ts | 8 +++ src/lib/components/ui/label/index.ts | 7 ++ src/lib/components/ui/label/label.svelte | 20 ++++++ src/lib/components/ui/sonner/index.ts | 1 + src/lib/components/ui/sonner/sonner.svelte | 13 ++++ src/routes/+layout.server.ts | 37 +++-------- src/routes/+layout.svelte | 4 ++ src/routes/+page.server.ts | 7 +- src/routes/login/+page.server.ts | 29 +++++++++ src/routes/login/+page.svelte | 76 ++++++++++++++++++++++ src/routes/register/+page.server.ts | 29 +++++++++ src/routes/register/+page.svelte | 50 ++++++++++++++ 16 files changed, 293 insertions(+), 40 deletions(-) create mode 100644 src/lib/Navbar.svelte create mode 100644 src/lib/components/ui/label/index.ts create mode 100644 src/lib/components/ui/label/label.svelte create mode 100644 src/lib/components/ui/sonner/index.ts create mode 100644 src/lib/components/ui/sonner/sonner.svelte create mode 100644 src/routes/login/+page.server.ts create mode 100644 src/routes/login/+page.svelte create mode 100644 src/routes/register/+page.server.ts create mode 100644 src/routes/register/+page.svelte diff --git a/bun.lock b/bun.lock index e0a2924..c1d98d6 100644 --- a/bun.lock +++ b/bun.lock @@ -7,23 +7,24 @@ "dayjs": "^1.11.13", "jdenticon": "^3.3.0", "jsonwebtoken": "^9.0.2", - "mode-watcher": "0.5.1", + "mode-watcher": "^1.0.6", }, "devDependencies": { - "@internationalized/date": "^3.5.6", + "@internationalized/date": "^3.8.1", "@lucide/svelte": "^0.513.0", "@sveltejs/adapter-auto": "^6.0.0", "@sveltejs/kit": "^2.16.0", "@sveltejs/vite-plugin-svelte": "^5.0.0", "@tailwindcss/vite": "^4.0.0", "@types/jsonwebtoken": "^9.0.9", - "bits-ui": "^2.4.1", + "bits-ui": "^2.5.0", "clsx": "^2.1.1", "prettier": "^3.4.2", "prettier-plugin-svelte": "^3.3.3", "prettier-plugin-tailwindcss": "^0.6.11", "svelte": "^5.0.0", "svelte-check": "^4.0.0", + "svelte-sonner": "^1.0.4", "tailwind-merge": "^3.0.2", "tailwind-variants": "^1.0.0", "tailwindcss": "^4.0.0", @@ -208,7 +209,7 @@ "axobject-query": ["axobject-query@4.1.0", "", {}, "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ=="], - "bits-ui": ["bits-ui@2.4.1", "", { "dependencies": { "@floating-ui/core": "^1.7.0", "@floating-ui/dom": "^1.7.0", "css.escape": "^1.5.1", "esm-env": "^1.1.2", "runed": "^0.28.0", "svelte-toolbelt": "^0.9.1", "tabbable": "^6.2.0" }, "peerDependencies": { "@internationalized/date": "^3.8.1", "svelte": "^5.33.0" } }, "sha512-Z0qZkPgtxP5dkEOyZqCK2PQ1oFXsD0AlfFMtsrMLJqaYtjUBoQGosHVKEH03M9q4G8kxVnG74Zb0F9pS0X4+6w=="], + "bits-ui": ["bits-ui@2.5.0", "", { "dependencies": { "@floating-ui/core": "^1.7.0", "@floating-ui/dom": "^1.7.0", "css.escape": "^1.5.1", "esm-env": "^1.1.2", "runed": "^0.28.0", "svelte-toolbelt": "^0.9.1", "tabbable": "^6.2.0" }, "peerDependencies": { "@internationalized/date": "^3.8.1", "svelte": "^5.33.0" } }, "sha512-PbjylA1UWd4A/c5AYqie/EVxQ1/8uugmJKLg9whLoBBHbfPEBGhK09dCPrahK9kA6DRHhMmij0XXIUGIfrmNow=="], "buffer-equal-constant-time": ["buffer-equal-constant-time@1.0.1", "", {}, "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA=="], @@ -312,7 +313,7 @@ "mkdirp": ["mkdirp@3.0.1", "", { "bin": { "mkdirp": "dist/cjs/src/bin.js" } }, "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg=="], - "mode-watcher": ["mode-watcher@0.5.1", "", { "peerDependencies": { "svelte": "^4.0.0 || ^5.0.0-next.1" } }, "sha512-adEC6T7TMX/kzQlaO/MtiQOSFekZfQu4MC+lXyoceQG+U5sKpJWZ4yKXqw846ExIuWJgedkOIPqAYYRk/xHm+w=="], + "mode-watcher": ["mode-watcher@1.0.7", "", { "dependencies": { "runed": "^0.25.0", "svelte-toolbelt": "^0.7.1" }, "peerDependencies": { "svelte": "^5.27.0" } }, "sha512-ZGA7ZGdOvBJeTQkzdBOnXSgTkO6U6iIFWJoyGCTt6oHNg9XP9NBvS26De+V4W2aqI+B0yYXUskFG2VnEo3zyMQ=="], "mri": ["mri@1.2.0", "", {}, "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA=="], @@ -358,6 +359,8 @@ "svelte-check": ["svelte-check@4.2.1", "", { "dependencies": { "@jridgewell/trace-mapping": "^0.3.25", "chokidar": "^4.0.1", "fdir": "^6.2.0", "picocolors": "^1.0.0", "sade": "^1.7.4" }, "peerDependencies": { "svelte": "^4.0.0 || ^5.0.0-next.0", "typescript": ">=5.0.0" }, "bin": { "svelte-check": "bin/svelte-check" } }, "sha512-e49SU1RStvQhoipkQ/aonDhHnG3qxHSBtNfBRb9pxVXoa+N7qybAo32KgA9wEb2PCYFNaDg7bZCdhLD1vHpdYA=="], + "svelte-sonner": ["svelte-sonner@1.0.4", "", { "dependencies": { "runed": "^0.26.0" }, "peerDependencies": { "svelte": "^5.0.0" } }, "sha512-ctm9jeV0Rf3im2J6RU1emccrJFjRSdNSPsLlxaF62TLZw9bB1D40U/U7+wqEgohJY/X7FBdghdj0BFQF/IqKPQ=="], + "svelte-toolbelt": ["svelte-toolbelt@0.9.1", "", { "dependencies": { "clsx": "^2.1.1", "runed": "^0.28.0", "style-to-object": "^1.0.8" }, "peerDependencies": { "svelte": "^5.30.2" } }, "sha512-wBX6MtYw/kpht80j5zLpxJyR9soLizXPIAIWEVd9llAi17SR44ZdG291bldjB7r/K5duC0opDFcuhk2cA1hb8g=="], "tabbable": ["tabbable@6.2.0", "", {}, "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew=="], @@ -404,6 +407,14 @@ "@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + "mode-watcher/runed": ["runed@0.25.0", "", { "dependencies": { "esm-env": "^1.0.0" }, "peerDependencies": { "svelte": "^5.7.0" } }, "sha512-7+ma4AG9FT2sWQEA0Egf6mb7PBT2vHyuHail1ie8ropfSjvZGtEAx8YTmUjv/APCsdRRxEVvArNjALk9zFSOrg=="], + + "mode-watcher/svelte-toolbelt": ["svelte-toolbelt@0.7.1", "", { "dependencies": { "clsx": "^2.1.1", "runed": "^0.23.2", "style-to-object": "^1.0.8" }, "peerDependencies": { "svelte": "^5.0.0" } }, "sha512-HcBOcR17Vx9bjaOceUvxkY3nGmbBmCBBbuWLLEWO6jtmWH8f/QoWmbyUfQZrpDINH39en1b8mptfPQT9VKQ1xQ=="], + + "svelte-sonner/runed": ["runed@0.26.0", "", { "dependencies": { "esm-env": "^1.0.0" }, "peerDependencies": { "svelte": "^5.7.0" } }, "sha512-qWFv0cvLVRd8pdl/AslqzvtQyEn5KaIugEernwg9G98uJVSZcs/ygvPBvF80LA46V8pwRvSKnaVLDI3+i2wubw=="], + "tailwind-variants/tailwind-merge": ["tailwind-merge@3.0.2", "", {}, "sha512-l7z+OYZ7mu3DTqrL88RiKrKIqO3NcpEO8V/Od04bNpvk0kiIFndGEoqfuzvj4yuhRkHKjRkII2z+KS2HfPcSxw=="], + + "mode-watcher/svelte-toolbelt/runed": ["runed@0.23.4", "", { "dependencies": { "esm-env": "^1.0.0" }, "peerDependencies": { "svelte": "^5.7.0" } }, "sha512-9q8oUiBYeXIDLWNK5DfCWlkL0EW3oGbk845VdKlPeia28l751VpfesaB/+7pI6rnbx1I6rqoZ2fZxptOJLxILA=="], } } diff --git a/components.json b/components.json index 897ab35..c5d91b4 100644 --- a/components.json +++ b/components.json @@ -1,5 +1,5 @@ { - "$schema": "https://next.shadcn-svelte.com/schema.json", + "$schema": "https://shadcn-svelte.com/schema.json", "tailwind": { "css": "src/app.css", "baseColor": "slate" @@ -12,5 +12,5 @@ "lib": "$lib" }, "typescript": true, - "registry": "https://next.shadcn-svelte.com/registry" + "registry": "https://shadcn-svelte.com/registry" } diff --git a/package.json b/package.json index 7eb7631..b2c97cf 100644 --- a/package.json +++ b/package.json @@ -14,20 +14,21 @@ "lint": "prettier --check ." }, "devDependencies": { - "@internationalized/date": "^3.5.6", + "@internationalized/date": "^3.8.1", "@lucide/svelte": "^0.513.0", "@sveltejs/adapter-auto": "^6.0.0", "@sveltejs/kit": "^2.16.0", "@sveltejs/vite-plugin-svelte": "^5.0.0", "@tailwindcss/vite": "^4.0.0", "@types/jsonwebtoken": "^9.0.9", - "bits-ui": "^2.4.1", + "bits-ui": "^2.5.0", "clsx": "^2.1.1", "prettier": "^3.4.2", "prettier-plugin-svelte": "^3.3.3", "prettier-plugin-tailwindcss": "^0.6.11", "svelte": "^5.0.0", "svelte-check": "^4.0.0", + "svelte-sonner": "^1.0.4", "tailwind-merge": "^3.0.2", "tailwind-variants": "^1.0.0", "tailwindcss": "^4.0.0", @@ -39,6 +40,6 @@ "dayjs": "^1.11.13", "jdenticon": "^3.3.0", "jsonwebtoken": "^9.0.2", - "mode-watcher": "0.5.1" + "mode-watcher": "^1.0.6" } } diff --git a/src/lib/Navbar.svelte b/src/lib/Navbar.svelte new file mode 100644 index 0000000..762176f --- /dev/null +++ b/src/lib/Navbar.svelte @@ -0,0 +1,20 @@ + + + diff --git a/src/lib/auth.ts b/src/lib/auth.ts index 8b4aa37..4d30402 100644 --- a/src/lib/auth.ts +++ b/src/lib/auth.ts @@ -1 +1,9 @@ +import { decode, type JwtPayload } from 'jsonwebtoken'; + export const AUTH_COOIKIE_NAME: string = 'auth-token' as const; + +export function getUserIdFromToken(token: string): number { + const decoded = decode(token) as JwtPayload; + + return decoded.userId; +} diff --git a/src/lib/components/ui/label/index.ts b/src/lib/components/ui/label/index.ts new file mode 100644 index 0000000..8bfca0b --- /dev/null +++ b/src/lib/components/ui/label/index.ts @@ -0,0 +1,7 @@ +import Root from "./label.svelte"; + +export { + Root, + // + Root as Label, +}; diff --git a/src/lib/components/ui/label/label.svelte b/src/lib/components/ui/label/label.svelte new file mode 100644 index 0000000..d0afda3 --- /dev/null +++ b/src/lib/components/ui/label/label.svelte @@ -0,0 +1,20 @@ + + + diff --git a/src/lib/components/ui/sonner/index.ts b/src/lib/components/ui/sonner/index.ts new file mode 100644 index 0000000..1ad9f4a --- /dev/null +++ b/src/lib/components/ui/sonner/index.ts @@ -0,0 +1 @@ +export { default as Toaster } from "./sonner.svelte"; diff --git a/src/lib/components/ui/sonner/sonner.svelte b/src/lib/components/ui/sonner/sonner.svelte new file mode 100644 index 0000000..67669b7 --- /dev/null +++ b/src/lib/components/ui/sonner/sonner.svelte @@ -0,0 +1,13 @@ + + + diff --git a/src/routes/+layout.server.ts b/src/routes/+layout.server.ts index 8b0f8fe..b4869ca 100644 --- a/src/routes/+layout.server.ts +++ b/src/routes/+layout.server.ts @@ -1,43 +1,22 @@ -import { PUBLIC_BACKEND_URL } from '$env/static/public'; -import { AUTH_COOIKIE_NAME } from '$lib/auth'; -import { error } from '@sveltejs/kit'; +import { AUTH_COOIKIE_NAME, getUserIdFromToken } from '$lib/auth'; +import { redirect } from '@sveltejs/kit'; import type { LayoutServerLoad } from './$types'; -import { decode, type JwtPayload } from 'jsonwebtoken'; -export const load: LayoutServerLoad = async ({ fetch, cookies }) => { +export const load: LayoutServerLoad = async ({ cookies, url }) => { let token = cookies.get(AUTH_COOIKIE_NAME); - if (token != null) { - const userId = getUserIdFromToken(token); + if (token == null) { + if (url.pathname !== '/register' && url.pathname !== '/login') { + return redirect(303, '/login'); + } - return { - token, - userId - }; + return { }; } - const response = await fetch(`${PUBLIC_BACKEND_URL}/auth`); - - if (!response.ok) { - return error(response.status); - } - - token = await response.text(); const userId = getUserIdFromToken(token); - cookies.set(AUTH_COOIKIE_NAME, token, { - path: '/', - secure: true - }); - return { token, userId }; }; - -function getUserIdFromToken(token: string): number { - const decoded = decode(token) as JwtPayload; - - return decoded.userId; -} diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index 852ed6e..624215c 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -6,6 +6,8 @@ import { browser } from '$app/environment'; import { setMessageStore } from '$lib/message.svelte'; import type { LayoutServerData } from './$types.js'; + import Navbar from '$lib/Navbar.svelte'; + import { Toaster } from '$lib/components/ui/sonner'; interface Props { children: Snippet; @@ -26,8 +28,10 @@ +
+
{@render children()}
diff --git a/src/routes/+page.server.ts b/src/routes/+page.server.ts index 03e6236..3aff863 100644 --- a/src/routes/+page.server.ts +++ b/src/routes/+page.server.ts @@ -1,4 +1,4 @@ -import { error, fail, type Actions } from '@sveltejs/kit'; +import { error, fail, redirect, type Actions } from '@sveltejs/kit'; import { PUBLIC_BACKEND_URL } from '$env/static/public'; import { AUTH_COOIKIE_NAME } from '$lib/auth'; import type { PageServerLoad } from './$types'; @@ -47,5 +47,10 @@ export const actions = { }, body: formData }); + }, + logout: async ({ cookies }) => { + cookies.delete(AUTH_COOIKIE_NAME, { path: '/' }); + + redirect(303, '/login'); } } satisfies Actions; diff --git a/src/routes/login/+page.server.ts b/src/routes/login/+page.server.ts new file mode 100644 index 0000000..6bb2ebf --- /dev/null +++ b/src/routes/login/+page.server.ts @@ -0,0 +1,29 @@ +import { PUBLIC_BACKEND_URL } from "$env/static/public"; +import { fail, redirect } from "@sveltejs/kit"; +import type { Actions } from "./$types"; +import { AUTH_COOIKIE_NAME } from "$lib/auth"; + +export const actions = { + login: async ({ request, fetch, cookies }) => { + const formData = await request.formData(); + + const response = await fetch(`${PUBLIC_BACKEND_URL}/login`, { + method: 'POST', + body: formData, + }); + + if (!response.ok) { + return fail(response.status, { message: 'Username or password are incorrect' }); + } + + const token = await response.text(); + + cookies.set(AUTH_COOIKIE_NAME, token, { + path: '/', + secure: true + }); + + return redirect(303, '/'); + }, +} satisfies Actions; + diff --git a/src/routes/login/+page.svelte b/src/routes/login/+page.svelte new file mode 100644 index 0000000..d2c8eaa --- /dev/null +++ b/src/routes/login/+page.svelte @@ -0,0 +1,76 @@ + + +
+

Login

+ +
+ + +
+ +
+ + +
+ +
+ Register instead? + +
+
diff --git a/src/routes/register/+page.server.ts b/src/routes/register/+page.server.ts new file mode 100644 index 0000000..8b0fd7b --- /dev/null +++ b/src/routes/register/+page.server.ts @@ -0,0 +1,29 @@ +import { PUBLIC_BACKEND_URL } from "$env/static/public"; +import { fail, redirect } from "@sveltejs/kit"; +import type { Actions } from "./$types"; +import { AUTH_COOIKIE_NAME } from "$lib/auth"; + +export const actions = { + register: async ({ request, fetch, cookies }) => { + const formData = await request.formData(); + + const response = await fetch(`${PUBLIC_BACKEND_URL}/register`, { + method: 'POST', + body: formData, + }); + + if (!response.ok) { + return fail(response.status); + } + + const token = await response.text(); + + cookies.set(AUTH_COOIKIE_NAME, token, { + path: '/', + secure: true + }); + + return redirect(303, '/'); + }, +} satisfies Actions; + diff --git a/src/routes/register/+page.svelte b/src/routes/register/+page.svelte new file mode 100644 index 0000000..d5e321f --- /dev/null +++ b/src/routes/register/+page.svelte @@ -0,0 +1,50 @@ + + +
+

Register

+ +
+ + +
+ +
+ + +
+ +
+ Login instead? + +
+