diff --git a/package-lock.json b/package-lock.json index 46d2452..16a75f8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,8 +10,10 @@ "devDependencies": { "@eslint/compat": "^1.2.5", "@eslint/js": "^9.18.0", + "@ethercorps/sveltekit-redis-session": "^1.3.1", "@sveltejs/adapter-auto": "^4.0.0", "@sveltejs/adapter-node": "^5.2.12", + "@sveltejs/adapter-static": "^3.0.8", "@sveltejs/kit": "^2.16.0", "@sveltejs/vite-plugin-svelte": "^5.0.0", "@tailwindcss/postcss": "^4.0.4", @@ -20,6 +22,7 @@ "eslint-config-prettier": "^10.0.1", "eslint-plugin-svelte": "^2.46.1", "globals": "^15.14.0", + "ioredis": "^5.3.2", "prettier": "^3.4.2", "prettier-plugin-svelte": "^3.3.3", "prettier-plugin-tailwindcss": "^0.6.11", @@ -28,6 +31,7 @@ "tailwindcss": "^4.0.0", "typescript": "^5.0.0", "typescript-eslint": "^8.20.0", + "uuid": "^11.0.5", "vite": "^6.0.0" } }, @@ -642,6 +646,30 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, + "node_modules/@ethercorps/sveltekit-redis-session": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@ethercorps/sveltekit-redis-session/-/sveltekit-redis-session-1.3.1.tgz", + "integrity": "sha512-t8obR+dZ1ln28QZjTlXSnA6ydYNa8qdjEgSfK9BHETgvcdUXlC2XZjzaQKY8udJGMMErLR9HpXr80vuJmrQDcQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@sveltejs/kit": "^1.23.0 || ^2.0.0", + "@upstash/redis": "^1.22.0", + "ioredis": "^5.3.2", + "redis": "^4.6.8" + }, + "peerDependenciesMeta": { + "@upstash/redis": { + "optional": true + }, + "ioredis": { + "optional": true + }, + "redis": { + "optional": true + } + } + }, "node_modules/@humanfs/core": { "version": "0.19.1", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", @@ -708,6 +736,13 @@ "url": "https://github.com/sponsors/nzakas" } }, + "node_modules/@ioredis/commands": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.2.0.tgz", + "integrity": "sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg==", + "dev": true, + "license": "MIT" + }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.8", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", @@ -806,6 +841,83 @@ "dev": true, "license": "MIT" }, + "node_modules/@redis/bloom": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-1.2.0.tgz", + "integrity": "sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "node_modules/@redis/client": { + "version": "1.5.9", + "resolved": "https://registry.npmjs.org/@redis/client/-/client-1.5.9.tgz", + "integrity": "sha512-SffgN+P1zdWJWSXBvJeynvEnmnZrYmtKSRW00xl8pOPFOMJjxRR9u0frSxJpPR6Y4V+k54blJjGW7FgxbTI7bQ==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "cluster-key-slot": "1.1.2", + "generic-pool": "3.9.0", + "yallist": "4.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@redis/graph": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@redis/graph/-/graph-1.1.0.tgz", + "integrity": "sha512-16yZWngxyXPd+MJxeSr0dqh2AIOi8j9yXKcKCwVaKDbH3HTuETpDVPcLujhFYVPtYrngSco31BUcSa9TH31Gqg==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "node_modules/@redis/json": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@redis/json/-/json-1.0.4.tgz", + "integrity": "sha512-LUZE2Gdrhg0Rx7AN+cZkb1e6HjoSKaeeW8rYnt89Tly13GBI5eP4CwDVr+MY8BAYfCg4/N15OUrtLoona9uSgw==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "node_modules/@redis/search": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@redis/search/-/search-1.1.3.tgz", + "integrity": "sha512-4Dg1JjvCevdiCBTZqjhKkGoC5/BcB7k9j99kdMnaXFXg8x4eyOIVg9487CMv7/BUVkFLZCaIh8ead9mU15DNng==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "node_modules/@redis/time-series": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-1.0.5.tgz", + "integrity": "sha512-IFjIgTusQym2B5IZJG3XKr5llka7ey84fw/NOYqESP5WUfQs9zz1ww/9+qoz4ka/S6KcGBodzlCeZ5UImKbscg==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, "node_modules/@rollup/plugin-commonjs": { "version": "28.0.2", "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-28.0.2.tgz", @@ -1207,6 +1319,16 @@ "@sveltejs/kit": "^2.4.0" } }, + "node_modules/@sveltejs/adapter-static": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@sveltejs/adapter-static/-/adapter-static-3.0.8.tgz", + "integrity": "sha512-YaDrquRpZwfcXbnlDsSrBQNCChVOT9MGuSg+dMAyfsAa1SmiAhrA5jUYUiIMC59G92kIbY/AaQOWcBdq+lh+zg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@sveltejs/kit": "^2.0.0" + } + }, "node_modules/@sveltejs/kit": { "version": "2.17.1", "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.17.1.tgz", @@ -1942,6 +2064,16 @@ "node": ">=6" } }, + "node_modules/cluster-key-slot": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", + "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -2049,6 +2181,16 @@ "node": ">=0.10.0" } }, + "node_modules/denque": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", + "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=0.10" + } + }, "node_modules/detect-libc": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", @@ -2544,6 +2686,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/generic-pool": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.9.0.tgz", + "integrity": "sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">= 4" + } + }, "node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", @@ -2655,6 +2809,31 @@ "node": ">=0.8.19" } }, + "node_modules/ioredis": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.3.2.tgz", + "integrity": "sha512-1DKMMzlIHM02eBBVOFQ1+AolGjs6+xEcM4PDL7NqOS6szq7H9jSaEkIUH6/a5Hl241LzW6JLSiAbNvTQjUupUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ioredis/commands": "^1.1.1", + "cluster-key-slot": "^1.1.0", + "debug": "^4.3.4", + "denque": "^2.1.0", + "lodash.defaults": "^4.2.0", + "lodash.isarguments": "^3.1.0", + "redis-errors": "^1.2.0", + "redis-parser": "^3.0.0", + "standard-as-callback": "^2.1.0" + }, + "engines": { + "node": ">=12.22.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/ioredis" + } + }, "node_modules/is-core-module": { "version": "2.16.1", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", @@ -3085,6 +3264,20 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lodash.defaults": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", + "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isarguments": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", + "integrity": "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==", + "dev": true, + "license": "MIT" + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -3603,6 +3796,49 @@ "url": "https://paulmillr.com/funding/" } }, + "node_modules/redis": { + "version": "4.6.8", + "resolved": "https://registry.npmjs.org/redis/-/redis-4.6.8.tgz", + "integrity": "sha512-S7qNkPUYrsofQ0ztWlTHSaK0Qqfl1y+WMIxrzeAGNG+9iUZB4HGeBgkHxE6uJJ6iXrkvLd1RVJ2nvu6H1sAzfQ==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "workspaces": [ + "./packages/*" + ], + "dependencies": { + "@redis/bloom": "1.2.0", + "@redis/client": "1.5.9", + "@redis/graph": "1.1.0", + "@redis/json": "1.0.4", + "@redis/search": "1.1.3", + "@redis/time-series": "1.0.5" + } + }, + "node_modules/redis-errors": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", + "integrity": "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/redis-parser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", + "integrity": "sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==", + "dev": true, + "license": "MIT", + "dependencies": { + "redis-errors": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/resolve": { "version": "1.22.10", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", @@ -3789,6 +4025,13 @@ "node": ">=0.10.0" } }, + "node_modules/standard-as-callback": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz", + "integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==", + "dev": true, + "license": "MIT" + }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -4074,6 +4317,20 @@ "dev": true, "license": "MIT" }, + "node_modules/uuid": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.0.5.tgz", + "integrity": "sha512-508e6IcKLrhxKdBbcA2b4KQZlLVp2+J5UwQ6F7Drckkc5N9ZJwFa4TgWtsww9UG8fGHbm6gbV19TdM5pQ4GaIA==", + "dev": true, + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/esm/bin/uuid" + } + }, "node_modules/vite": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/vite/-/vite-6.1.0.tgz", @@ -4191,6 +4448,15 @@ "node": ">=0.10.0" } }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "license": "ISC", + "optional": true, + "peer": true + }, "node_modules/yaml": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.0.tgz", diff --git a/package.json b/package.json index b7f1783..f8f62e5 100644 --- a/package.json +++ b/package.json @@ -16,8 +16,10 @@ "devDependencies": { "@eslint/compat": "^1.2.5", "@eslint/js": "^9.18.0", + "@ethercorps/sveltekit-redis-session": "^1.3.1", "@sveltejs/adapter-auto": "^4.0.0", "@sveltejs/adapter-node": "^5.2.12", + "@sveltejs/adapter-static": "^3.0.8", "@sveltejs/kit": "^2.16.0", "@sveltejs/vite-plugin-svelte": "^5.0.0", "@tailwindcss/postcss": "^4.0.4", @@ -26,6 +28,7 @@ "eslint-config-prettier": "^10.0.1", "eslint-plugin-svelte": "^2.46.1", "globals": "^15.14.0", + "ioredis": "^5.3.2", "prettier": "^3.4.2", "prettier-plugin-svelte": "^3.3.3", "prettier-plugin-tailwindcss": "^0.6.11", @@ -34,6 +37,7 @@ "tailwindcss": "^4.0.0", "typescript": "^5.0.0", "typescript-eslint": "^8.20.0", + "uuid": "^11.0.5", "vite": "^6.0.0" } } diff --git a/src/api/apiService.ts b/src/api/apiService.ts index eb280e5..7db7772 100644 --- a/src/api/apiService.ts +++ b/src/api/apiService.ts @@ -1,8 +1,9 @@ import { authStore } from '$stores/authStore'; import { redirect } from '@sveltejs/kit'; -import type { RequestEvent } from '../routes/$types'; +import { redisSessionManager } from '$stores/redisSessionManager'; +import { API_BASE_URL } from '$env/static/private'; -const BASE_URL = import.meta.env.VITE_API_BASE_URL || 'https://acc-api.jurmanovic.com/v1'; +const BASE_URL = API_BASE_URL; async function fetchAPI(endpoint: string, method: string = 'GET', body?: object, hdr?: object) { const headers = { @@ -39,7 +40,9 @@ export async function fetchAPIEvent( method: string = 'GET', body?: object ) { - const token = event.cookies.get('token'); + const { + data: { token } + } = await redisSessionManager.getSession(event.cookies); return fetchAPI(endpoint, method, body, { Authorization: `Basic ${token}` }); } diff --git a/src/api/authService.ts b/src/api/authService.ts index ea5e6ab..157c124 100644 --- a/src/api/authService.ts +++ b/src/api/authService.ts @@ -1,10 +1,11 @@ import fetchAPI, { fetchAPIEvent } from '$api/apiService'; import { authStore } from '$stores/authStore'; -import type { RequestEvent } from '../routes/$types'; +import { redisSessionManager } from '$stores/redisSessionManager'; +import { v4 as uuidv4 } from 'uuid'; export const login = async (event: object, username: string, password: string) => { const token = btoa(`${username}:${password}`); - event.cookies.set('token', token, { path: '/' }); + await redisSessionManager.createSession(event.cookies, { token }, uuidv4()); if (!(await checkAuth(event))) { { authStore.set({ token: undefined, error: 'Invalid username or password.' }); @@ -15,7 +16,7 @@ export const login = async (event: object, username: string, password: string) = }; export const logout = (event) => { - event.cookies.delete('token', { path: '/' }); + return redisSessionManager.deleteCookie(event.cookies); }; export const checkAuth = async (event: object) => { diff --git a/src/components/Sidebar.svelte b/src/components/Sidebar.svelte index 36ef4e6..01ad1ae 100644 --- a/src/components/Sidebar.svelte +++ b/src/components/Sidebar.svelte @@ -30,7 +30,7 @@