Auth is actually working
This commit is contained in:
parent
32281aa1ff
commit
15ff9609c4
@ -11,7 +11,7 @@ localhost {
|
||||
|
||||
handle {
|
||||
reverse_proxy dev:5173 {
|
||||
header_up Host {host}
|
||||
header_up Origin "^https://localhost$" "http://localhost"
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
CREATE TABLE "sessions" (
|
||||
"id" integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY (sequence name "sessions_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 START WITH 1 CACHE 1),
|
||||
"user_id" integer,
|
||||
"id" text PRIMARY KEY NOT NULL,
|
||||
"user_id" integer NOT NULL,
|
||||
"expires_at" timestamp with time zone NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"id": "f22311a8-ce92-4bd5-9a7c-5e8085af13a9",
|
||||
"id": "9f31c19a-9e3c-41db-a08b-27d9916265cb",
|
||||
"prevId": "00000000-0000-0000-0000-000000000000",
|
||||
"version": "7",
|
||||
"dialect": "postgresql",
|
||||
@ -10,26 +10,15 @@
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "integer",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"identity": {
|
||||
"type": "always",
|
||||
"name": "sessions_id_seq",
|
||||
"schema": "public",
|
||||
"increment": "1",
|
||||
"startWith": "1",
|
||||
"minValue": "1",
|
||||
"maxValue": "2147483647",
|
||||
"cache": "1",
|
||||
"cycle": false
|
||||
}
|
||||
"notNull": true
|
||||
},
|
||||
"user_id": {
|
||||
"name": "user_id",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
"notNull": true
|
||||
},
|
||||
"expires_at": {
|
||||
"name": "expires_at",
|
||||
|
@ -5,7 +5,7 @@
|
||||
{
|
||||
"idx": 0,
|
||||
"version": "7",
|
||||
"when": 1738177092891,
|
||||
"when": 1738197417069,
|
||||
"tag": "0000_users-and-sessions",
|
||||
"breakpoints": true
|
||||
}
|
||||
|
8
index.js
8
index.js
@ -1,8 +0,0 @@
|
||||
import { loadEnv } from 'vite';
|
||||
|
||||
console.log(process.env);
|
||||
|
||||
const env = loadEnv("development", ".", "");
|
||||
|
||||
// console.log(env);
|
||||
// console.log(process.env)
|
10
package.json
10
package.json
@ -16,11 +16,14 @@
|
||||
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
||||
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
||||
"test:unit": "vitest",
|
||||
"test": "npm run test:unit -- --run"
|
||||
"test": "npm run test:unit -- --run",
|
||||
"db:push": "drizzle-kit push",
|
||||
"db:migrate": "drizzle-kit migrate",
|
||||
"db:studio": "drizzle-kit studio"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "1.9.4",
|
||||
"@sveltejs/adapter-auto": "^4.0.0",
|
||||
"@sveltejs/adapter-node": "^5.2.12",
|
||||
"@sveltejs/kit": "^2.16.1",
|
||||
"@sveltejs/vite-plugin-svelte": "^5.0.3",
|
||||
"@types/pg": "^8.11.11",
|
||||
@ -36,7 +39,10 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@devcontainers/cli": "^0.73.0",
|
||||
"@js-temporal/polyfill": "^0.4.4",
|
||||
"@node-rs/argon2": "^2.0.2",
|
||||
"@oslojs/crypto": "^1.0.1",
|
||||
"@oslojs/encoding": "^1.1.0",
|
||||
"drizzle-orm": "^0.39.0",
|
||||
"pg": "^8.13.1"
|
||||
}
|
||||
|
215
pnpm-lock.yaml
generated
215
pnpm-lock.yaml
generated
@ -11,9 +11,18 @@ importers:
|
||||
'@devcontainers/cli':
|
||||
specifier: ^0.73.0
|
||||
version: 0.73.0
|
||||
'@js-temporal/polyfill':
|
||||
specifier: ^0.4.4
|
||||
version: 0.4.4
|
||||
'@node-rs/argon2':
|
||||
specifier: ^2.0.2
|
||||
version: 2.0.2
|
||||
'@oslojs/crypto':
|
||||
specifier: ^1.0.1
|
||||
version: 1.0.1
|
||||
'@oslojs/encoding':
|
||||
specifier: ^1.1.0
|
||||
version: 1.1.0
|
||||
drizzle-orm:
|
||||
specifier: ^0.39.0
|
||||
version: 0.39.0(@types/pg@8.11.11)(pg@8.13.1)
|
||||
@ -24,9 +33,9 @@ importers:
|
||||
'@biomejs/biome':
|
||||
specifier: 1.9.4
|
||||
version: 1.9.4
|
||||
'@sveltejs/adapter-auto':
|
||||
specifier: ^4.0.0
|
||||
version: 4.0.0(@sveltejs/kit@2.16.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.19.5)(vite@6.0.11(@types/node@22.12.0)(tsx@4.19.2)))(svelte@5.19.5)(vite@6.0.11(@types/node@22.12.0)(tsx@4.19.2)))
|
||||
'@sveltejs/adapter-node':
|
||||
specifier: ^5.2.12
|
||||
version: 5.2.12(@sveltejs/kit@2.16.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.19.5)(vite@6.0.11(@types/node@22.12.0)(tsx@4.19.2)))(svelte@5.19.5)(vite@6.0.11(@types/node@22.12.0)(tsx@4.19.2)))
|
||||
'@sveltejs/kit':
|
||||
specifier: ^2.16.1
|
||||
version: 2.16.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.19.5)(vite@6.0.11(@types/node@22.12.0)(tsx@4.19.2)))(svelte@5.19.5)(vite@6.0.11(@types/node@22.12.0)(tsx@4.19.2))
|
||||
@ -730,6 +739,10 @@ packages:
|
||||
'@jridgewell/trace-mapping@0.3.25':
|
||||
resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==}
|
||||
|
||||
'@js-temporal/polyfill@0.4.4':
|
||||
resolution: {integrity: sha512-2X6bvghJ/JAoZO52lbgyAPFj8uCflhTo2g7nkFzEQdXd/D8rEeD4HtmTEpmtGCva260fcd66YNXBOYdnmHqSOg==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
'@napi-rs/wasm-runtime@0.2.6':
|
||||
resolution: {integrity: sha512-z8YVS3XszxFTO73iwvFDNpQIzdMmSDTP/mB3E/ucR37V3Sx57hSExcXyMoNwaucWxnsWf4xfbZv0iZ30jr0M4Q==}
|
||||
|
||||
@ -820,9 +833,57 @@ packages:
|
||||
resolution: {integrity: sha512-t64wIsPEtNd4aUPuTAyeL2ubxATCBGmeluaKXEMAFk/8w6AJIVVkeLKMBpgLW6LU2t5cQxT+env/c6jxbtTQBg==}
|
||||
engines: {node: '>= 10'}
|
||||
|
||||
'@oslojs/asn1@1.0.0':
|
||||
resolution: {integrity: sha512-zw/wn0sj0j0QKbIXfIlnEcTviaCzYOY3V5rAyjR6YtOByFtJiT574+8p9Wlach0lZH9fddD4yb9laEAIl4vXQA==}
|
||||
|
||||
'@oslojs/binary@1.0.0':
|
||||
resolution: {integrity: sha512-9RCU6OwXU6p67H4NODbuxv2S3eenuQ4/WFLrsq+K/k682xrznH5EVWA7N4VFk9VYVcbFtKqur5YQQZc0ySGhsQ==}
|
||||
|
||||
'@oslojs/crypto@1.0.1':
|
||||
resolution: {integrity: sha512-7n08G8nWjAr/Yu3vu9zzrd0L9XnrJfpMioQcvCMxBIiF5orECHe5/3J0jmXRVvgfqMm/+4oxlQ+Sq39COYLcNQ==}
|
||||
|
||||
'@oslojs/encoding@1.1.0':
|
||||
resolution: {integrity: sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ==}
|
||||
|
||||
'@polka/url@1.0.0-next.28':
|
||||
resolution: {integrity: sha512-8LduaNlMZGwdZ6qWrKlfa+2M4gahzFkprZiAt2TF8uS0qQgBizKXpXURqvTJ4WtmupWxaLqjRb2UCTe72mu+Aw==}
|
||||
|
||||
'@rollup/plugin-commonjs@28.0.2':
|
||||
resolution: {integrity: sha512-BEFI2EDqzl+vA1rl97IDRZ61AIwGH093d9nz8+dThxJNH8oSoB7MjWvPCX3dkaK1/RCJ/1v/R1XB15FuSs0fQw==}
|
||||
engines: {node: '>=16.0.0 || 14 >= 14.17'}
|
||||
peerDependencies:
|
||||
rollup: ^2.68.0||^3.0.0||^4.0.0
|
||||
peerDependenciesMeta:
|
||||
rollup:
|
||||
optional: true
|
||||
|
||||
'@rollup/plugin-json@6.1.0':
|
||||
resolution: {integrity: sha512-EGI2te5ENk1coGeADSIwZ7G2Q8CJS2sF120T7jLw4xFw9n7wIOXHo+kIYRAoVpJAN+kmqZSoO3Fp4JtoNF4ReA==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
peerDependencies:
|
||||
rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0
|
||||
peerDependenciesMeta:
|
||||
rollup:
|
||||
optional: true
|
||||
|
||||
'@rollup/plugin-node-resolve@16.0.0':
|
||||
resolution: {integrity: sha512-0FPvAeVUT/zdWoO0jnb/V5BlBsUSNfkIOtFHzMO4H9MOklrmQFY6FduVHKucNb/aTFxvnGhj4MNj/T1oNdDfNg==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
peerDependencies:
|
||||
rollup: ^2.78.0||^3.0.0||^4.0.0
|
||||
peerDependenciesMeta:
|
||||
rollup:
|
||||
optional: true
|
||||
|
||||
'@rollup/pluginutils@5.1.4':
|
||||
resolution: {integrity: sha512-USm05zrsFxYLPdWWq+K3STlWiT/3ELn3RcV5hJMghpeAIhxfsUIg6mt12CBJBInWMV4VneoV7SfGv8xIwo2qNQ==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
peerDependencies:
|
||||
rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0
|
||||
peerDependenciesMeta:
|
||||
rollup:
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-android-arm-eabi@4.32.1':
|
||||
resolution: {integrity: sha512-/pqA4DmqyCm8u5YIDzIdlLcEmuvxb0v8fZdFhVMszSpDTgbQKdw3/mB3eMUHIbubtJ6F9j+LtmyCnHTEqIHyzA==}
|
||||
cpu: [arm]
|
||||
@ -918,10 +979,10 @@ packages:
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
|
||||
'@sveltejs/adapter-auto@4.0.0':
|
||||
resolution: {integrity: sha512-kmuYSQdD2AwThymQF0haQhM8rE5rhutQXG4LNbnbShwhMO4qQGnKaaTy+88DuNSuoQDi58+thpq8XpHc1+oEKQ==}
|
||||
'@sveltejs/adapter-node@5.2.12':
|
||||
resolution: {integrity: sha512-0bp4Yb3jKIEcZWVcJC/L1xXp9zzJS4hDwfb4VITAkfT4OVdkspSHsx7YhqJDbb2hgLl6R9Vs7VQR+fqIVOxPUQ==}
|
||||
peerDependencies:
|
||||
'@sveltejs/kit': ^2.0.0
|
||||
'@sveltejs/kit': ^2.4.0
|
||||
|
||||
'@sveltejs/kit@2.16.1':
|
||||
resolution: {integrity: sha512-2pF5sgGJx9brYZ/9nNDYnh5KX0JguPF14dnvvtf/MqrvlWrDj/e7Rk3LBJPecFLLK1GRs6ZniD24gFPqZm/NFw==}
|
||||
@ -962,6 +1023,9 @@ packages:
|
||||
'@types/pg@8.11.11':
|
||||
resolution: {integrity: sha512-kGT1qKM8wJQ5qlawUrEkXgvMSXoV213KfMGXcwfDwUIfUHXqXYXOfS1nE1LINRJVVVx5wCm70XnFlMHaIcQAfw==}
|
||||
|
||||
'@types/resolve@1.20.2':
|
||||
resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==}
|
||||
|
||||
'@vitest/expect@3.0.4':
|
||||
resolution: {integrity: sha512-Nm5kJmYw6P2BxhJPkO3eKKhGYKRsnqJqf+r0yOGRKpEP+bSCBDsjXgiu1/5QFrnPMEgzfC38ZEjvCFgaNBC0Eg==}
|
||||
|
||||
@ -1036,6 +1100,9 @@ packages:
|
||||
resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
commondir@1.0.1:
|
||||
resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==}
|
||||
|
||||
cookie@0.6.0:
|
||||
resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==}
|
||||
engines: {node: '>= 0.6'}
|
||||
@ -1198,6 +1265,9 @@ packages:
|
||||
esrap@1.4.3:
|
||||
resolution: {integrity: sha512-Xddc1RsoFJ4z9nR7W7BFaEPIp4UXoeQ0+077UdWLxbafMQFyU79sQJMk7kxNgRwQ9/aVgaKacCHC2pUACGwmYw==}
|
||||
|
||||
estree-walker@2.0.2:
|
||||
resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
|
||||
|
||||
estree-walker@3.0.3:
|
||||
resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==}
|
||||
|
||||
@ -1218,15 +1288,35 @@ packages:
|
||||
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
|
||||
os: [darwin]
|
||||
|
||||
function-bind@1.1.2:
|
||||
resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
|
||||
|
||||
get-tsconfig@4.10.0:
|
||||
resolution: {integrity: sha512-kGzZ3LWWQcGIAmg6iWvXn0ei6WDtV26wzHRMwDSzmAbcXrTEXxHy6IehI6/4eT6VRKyMP1eF1VqwrVUmE/LR7A==}
|
||||
|
||||
hasown@2.0.2:
|
||||
resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
import-meta-resolve@4.1.0:
|
||||
resolution: {integrity: sha512-I6fiaX09Xivtk+THaMfAwnA3MVA5Big1WHF1Dfx9hFuvNIWpXnorlkzhcQf6ehrqQiiZECRt1poOAkPmer3ruw==}
|
||||
|
||||
is-core-module@2.16.1:
|
||||
resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
is-module@1.0.0:
|
||||
resolution: {integrity: sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==}
|
||||
|
||||
is-reference@1.2.1:
|
||||
resolution: {integrity: sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==}
|
||||
|
||||
is-reference@3.0.3:
|
||||
resolution: {integrity: sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==}
|
||||
|
||||
jsbi@4.3.0:
|
||||
resolution: {integrity: sha512-SnZNcinB4RIcnEyZqFPdGPVgrg2AcnykiBy0sHVJQKHYeaLUvi3Exj+iaPpLnFVkDPZIV4U0yvgC9/R4uEAZ9g==}
|
||||
|
||||
kleur@4.1.5:
|
||||
resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==}
|
||||
engines: {node: '>=6'}
|
||||
@ -1259,6 +1349,9 @@ packages:
|
||||
obuf@1.1.2:
|
||||
resolution: {integrity: sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==}
|
||||
|
||||
path-parse@1.0.7:
|
||||
resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
|
||||
|
||||
pathe@2.0.2:
|
||||
resolution: {integrity: sha512-15Ztpk+nov8DR524R4BF7uEuzESgzUEAV4Ah7CUMNGXdE5ELuvxElxGXndBl32vMSsWa1jpNf22Z+Er3sKwq+w==}
|
||||
|
||||
@ -1361,6 +1454,11 @@ packages:
|
||||
resolve-pkg-maps@1.0.0:
|
||||
resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==}
|
||||
|
||||
resolve@1.22.10:
|
||||
resolution: {integrity: sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==}
|
||||
engines: {node: '>= 0.4'}
|
||||
hasBin: true
|
||||
|
||||
rollup@4.32.1:
|
||||
resolution: {integrity: sha512-z+aeEsOeEa3mEbS1Tjl6sAZ8NE3+AalQz1RJGj81M+fizusbdDMoEJwdJNHfaB40Scr4qNu+welOfes7maKonA==}
|
||||
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
|
||||
@ -1401,6 +1499,10 @@ packages:
|
||||
std-env@3.8.0:
|
||||
resolution: {integrity: sha512-Bc3YwwCB+OzldMxOXJIIvC6cPRWr/LxOp48CdQTOkPyk/t4JWWJbrilwBd7RJzKV8QW7tJkcgAmeuLLJugl5/w==}
|
||||
|
||||
supports-preserve-symlinks-flag@1.0.0:
|
||||
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
svelte-check@4.1.4:
|
||||
resolution: {integrity: sha512-v0j7yLbT29MezzaQJPEDwksybTE2Ups9rUxEXy92T06TiA0cbqcO8wAOwNUVkFW6B0hsYHA+oAX3BS8b/2oHtw==}
|
||||
engines: {node: '>= 18.0.0'}
|
||||
@ -1915,6 +2017,11 @@ snapshots:
|
||||
'@jridgewell/resolve-uri': 3.1.2
|
||||
'@jridgewell/sourcemap-codec': 1.5.0
|
||||
|
||||
'@js-temporal/polyfill@0.4.4':
|
||||
dependencies:
|
||||
jsbi: 4.3.0
|
||||
tslib: 2.8.1
|
||||
|
||||
'@napi-rs/wasm-runtime@0.2.6':
|
||||
dependencies:
|
||||
'@emnapi/core': 1.3.1
|
||||
@ -1983,8 +2090,57 @@ snapshots:
|
||||
'@node-rs/argon2-win32-ia32-msvc': 2.0.2
|
||||
'@node-rs/argon2-win32-x64-msvc': 2.0.2
|
||||
|
||||
'@oslojs/asn1@1.0.0':
|
||||
dependencies:
|
||||
'@oslojs/binary': 1.0.0
|
||||
|
||||
'@oslojs/binary@1.0.0': {}
|
||||
|
||||
'@oslojs/crypto@1.0.1':
|
||||
dependencies:
|
||||
'@oslojs/asn1': 1.0.0
|
||||
'@oslojs/binary': 1.0.0
|
||||
|
||||
'@oslojs/encoding@1.1.0': {}
|
||||
|
||||
'@polka/url@1.0.0-next.28': {}
|
||||
|
||||
'@rollup/plugin-commonjs@28.0.2(rollup@4.32.1)':
|
||||
dependencies:
|
||||
'@rollup/pluginutils': 5.1.4(rollup@4.32.1)
|
||||
commondir: 1.0.1
|
||||
estree-walker: 2.0.2
|
||||
fdir: 6.4.3(picomatch@4.0.2)
|
||||
is-reference: 1.2.1
|
||||
magic-string: 0.30.17
|
||||
picomatch: 4.0.2
|
||||
optionalDependencies:
|
||||
rollup: 4.32.1
|
||||
|
||||
'@rollup/plugin-json@6.1.0(rollup@4.32.1)':
|
||||
dependencies:
|
||||
'@rollup/pluginutils': 5.1.4(rollup@4.32.1)
|
||||
optionalDependencies:
|
||||
rollup: 4.32.1
|
||||
|
||||
'@rollup/plugin-node-resolve@16.0.0(rollup@4.32.1)':
|
||||
dependencies:
|
||||
'@rollup/pluginutils': 5.1.4(rollup@4.32.1)
|
||||
'@types/resolve': 1.20.2
|
||||
deepmerge: 4.3.1
|
||||
is-module: 1.0.0
|
||||
resolve: 1.22.10
|
||||
optionalDependencies:
|
||||
rollup: 4.32.1
|
||||
|
||||
'@rollup/pluginutils@5.1.4(rollup@4.32.1)':
|
||||
dependencies:
|
||||
'@types/estree': 1.0.6
|
||||
estree-walker: 2.0.2
|
||||
picomatch: 4.0.2
|
||||
optionalDependencies:
|
||||
rollup: 4.32.1
|
||||
|
||||
'@rollup/rollup-android-arm-eabi@4.32.1':
|
||||
optional: true
|
||||
|
||||
@ -2042,10 +2198,13 @@ snapshots:
|
||||
'@rollup/rollup-win32-x64-msvc@4.32.1':
|
||||
optional: true
|
||||
|
||||
'@sveltejs/adapter-auto@4.0.0(@sveltejs/kit@2.16.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.19.5)(vite@6.0.11(@types/node@22.12.0)(tsx@4.19.2)))(svelte@5.19.5)(vite@6.0.11(@types/node@22.12.0)(tsx@4.19.2)))':
|
||||
'@sveltejs/adapter-node@5.2.12(@sveltejs/kit@2.16.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.19.5)(vite@6.0.11(@types/node@22.12.0)(tsx@4.19.2)))(svelte@5.19.5)(vite@6.0.11(@types/node@22.12.0)(tsx@4.19.2)))':
|
||||
dependencies:
|
||||
'@rollup/plugin-commonjs': 28.0.2(rollup@4.32.1)
|
||||
'@rollup/plugin-json': 6.1.0(rollup@4.32.1)
|
||||
'@rollup/plugin-node-resolve': 16.0.0(rollup@4.32.1)
|
||||
'@sveltejs/kit': 2.16.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.19.5)(vite@6.0.11(@types/node@22.12.0)(tsx@4.19.2)))(svelte@5.19.5)(vite@6.0.11(@types/node@22.12.0)(tsx@4.19.2))
|
||||
import-meta-resolve: 4.1.0
|
||||
rollup: 4.32.1
|
||||
|
||||
'@sveltejs/kit@2.16.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.19.5)(vite@6.0.11(@types/node@22.12.0)(tsx@4.19.2)))(svelte@5.19.5)(vite@6.0.11(@types/node@22.12.0)(tsx@4.19.2))':
|
||||
dependencies:
|
||||
@ -2105,6 +2264,8 @@ snapshots:
|
||||
pg-protocol: 1.7.0
|
||||
pg-types: 4.0.2
|
||||
|
||||
'@types/resolve@1.20.2': {}
|
||||
|
||||
'@vitest/expect@3.0.4':
|
||||
dependencies:
|
||||
'@vitest/spy': 3.0.4
|
||||
@ -2177,6 +2338,8 @@ snapshots:
|
||||
|
||||
clsx@2.1.1: {}
|
||||
|
||||
commondir@1.0.1: {}
|
||||
|
||||
cookie@0.6.0: {}
|
||||
|
||||
debug@4.4.0:
|
||||
@ -2330,6 +2493,8 @@ snapshots:
|
||||
dependencies:
|
||||
'@jridgewell/sourcemap-codec': 1.5.0
|
||||
|
||||
estree-walker@2.0.2: {}
|
||||
|
||||
estree-walker@3.0.3:
|
||||
dependencies:
|
||||
'@types/estree': 1.0.6
|
||||
@ -2343,16 +2508,34 @@ snapshots:
|
||||
fsevents@2.3.3:
|
||||
optional: true
|
||||
|
||||
function-bind@1.1.2: {}
|
||||
|
||||
get-tsconfig@4.10.0:
|
||||
dependencies:
|
||||
resolve-pkg-maps: 1.0.0
|
||||
|
||||
hasown@2.0.2:
|
||||
dependencies:
|
||||
function-bind: 1.1.2
|
||||
|
||||
import-meta-resolve@4.1.0: {}
|
||||
|
||||
is-core-module@2.16.1:
|
||||
dependencies:
|
||||
hasown: 2.0.2
|
||||
|
||||
is-module@1.0.0: {}
|
||||
|
||||
is-reference@1.2.1:
|
||||
dependencies:
|
||||
'@types/estree': 1.0.6
|
||||
|
||||
is-reference@3.0.3:
|
||||
dependencies:
|
||||
'@types/estree': 1.0.6
|
||||
|
||||
jsbi@4.3.0: {}
|
||||
|
||||
kleur@4.1.5: {}
|
||||
|
||||
locate-character@3.0.0: {}
|
||||
@ -2373,6 +2556,8 @@ snapshots:
|
||||
|
||||
obuf@1.1.2: {}
|
||||
|
||||
path-parse@1.0.7: {}
|
||||
|
||||
pathe@2.0.2: {}
|
||||
|
||||
pathval@2.0.0: {}
|
||||
@ -2426,8 +2611,7 @@ snapshots:
|
||||
|
||||
picocolors@1.1.1: {}
|
||||
|
||||
picomatch@4.0.2:
|
||||
optional: true
|
||||
picomatch@4.0.2: {}
|
||||
|
||||
postcss@8.5.1:
|
||||
dependencies:
|
||||
@ -2461,6 +2645,12 @@ snapshots:
|
||||
|
||||
resolve-pkg-maps@1.0.0: {}
|
||||
|
||||
resolve@1.22.10:
|
||||
dependencies:
|
||||
is-core-module: 2.16.1
|
||||
path-parse: 1.0.7
|
||||
supports-preserve-symlinks-flag: 1.0.0
|
||||
|
||||
rollup@4.32.1:
|
||||
dependencies:
|
||||
'@types/estree': 1.0.6
|
||||
@ -2515,6 +2705,8 @@ snapshots:
|
||||
|
||||
std-env@3.8.0: {}
|
||||
|
||||
supports-preserve-symlinks-flag@1.0.0: {}
|
||||
|
||||
svelte-check@4.1.4(picomatch@4.0.2)(svelte@5.19.5)(typescript@5.7.3):
|
||||
dependencies:
|
||||
'@jridgewell/trace-mapping': 0.3.25
|
||||
@ -2556,8 +2748,7 @@ snapshots:
|
||||
|
||||
totalist@3.0.1: {}
|
||||
|
||||
tslib@2.8.1:
|
||||
optional: true
|
||||
tslib@2.8.1: {}
|
||||
|
||||
tsx@4.19.2:
|
||||
dependencies:
|
||||
|
70
src/hooks.server.ts
Normal file
70
src/hooks.server.ts
Normal file
@ -0,0 +1,70 @@
|
||||
import { RefillingTokenBucket } from "$lib/server/rate-limit";
|
||||
import {
|
||||
validateSessionToken,
|
||||
setSessionTokenCookie,
|
||||
deleteSessionTokenCookie,
|
||||
} from "$lib/server/session";
|
||||
import { sequence } from "@sveltejs/kit/hooks";
|
||||
|
||||
import { redirect, type Handle } from "@sveltejs/kit";
|
||||
|
||||
const bucket = new RefillingTokenBucket<string>(100, 1);
|
||||
|
||||
const rateLimitHandle: Handle = async ({ event, resolve }) => {
|
||||
// Note: Assumes X-Forwarded-For will always be defined.
|
||||
const clientIP = event.request.headers.get("X-Forwarded-For");
|
||||
if (clientIP === null) {
|
||||
return resolve(event);
|
||||
}
|
||||
let cost: number;
|
||||
if (event.request.method === "GET" || event.request.method === "OPTIONS") {
|
||||
cost = 1;
|
||||
} else {
|
||||
cost = 3;
|
||||
}
|
||||
if (!bucket.consume(clientIP, cost)) {
|
||||
return new Response("Too many requests", {
|
||||
status: 429,
|
||||
});
|
||||
}
|
||||
return resolve(event);
|
||||
};
|
||||
|
||||
const authHandle: Handle = async ({ event, resolve }) => {
|
||||
const token = event.cookies.get("session") ?? null;
|
||||
if (token === null) {
|
||||
event.locals.user = null;
|
||||
event.locals.session = null;
|
||||
return resolve(event);
|
||||
}
|
||||
|
||||
const { session, user } = await validateSessionToken(token);
|
||||
if (session !== null) {
|
||||
setSessionTokenCookie(event, token, session.expiresAt);
|
||||
} else {
|
||||
deleteSessionTokenCookie(event);
|
||||
}
|
||||
|
||||
event.locals.session = session;
|
||||
event.locals.user = user;
|
||||
return resolve(event);
|
||||
};
|
||||
|
||||
const enforceAuthenticated: Handle = async ({ event, resolve }) => {
|
||||
const routeExceptions = ["/login"];
|
||||
if (event.route.id !== null && routeExceptions.includes(event.route.id)) {
|
||||
return resolve(event);
|
||||
}
|
||||
|
||||
if (!(event.locals.session && event.locals.user)) {
|
||||
redirect(303, "/login");
|
||||
}
|
||||
|
||||
return resolve(event);
|
||||
};
|
||||
|
||||
export const handle = sequence(
|
||||
rateLimitHandle,
|
||||
authHandle,
|
||||
enforceAuthenticated,
|
||||
);
|
@ -1,10 +1,10 @@
|
||||
import { DATABASE_PASSWORD } from "$env/static/private";
|
||||
import { DATABASE_URL } from "$env/static/private";
|
||||
import { drizzle } from "drizzle-orm/node-postgres";
|
||||
|
||||
import * as schema from "./schema";
|
||||
|
||||
export const db = drizzle({
|
||||
connection: `postgres://postgres:${DATABASE_PASSWORD}@database/kredens`,
|
||||
connection: DATABASE_URL,
|
||||
casing: "snake_case",
|
||||
schema,
|
||||
});
|
||||
|
@ -1,15 +1,30 @@
|
||||
import { integer, pgTable, varchar, text, timestamp } from "drizzle-orm/pg-core";
|
||||
import { relations } from "drizzle-orm";
|
||||
import {
|
||||
integer,
|
||||
pgTable,
|
||||
varchar,
|
||||
text,
|
||||
timestamp,
|
||||
} from "drizzle-orm/pg-core";
|
||||
|
||||
export const users = pgTable("users", {
|
||||
id: integer().primaryKey().generatedAlwaysAsIdentity(),
|
||||
username: varchar({length: 128, }).unique().notNull(),
|
||||
password_hash: text(),
|
||||
username: varchar({ length: 128 }).unique().notNull(),
|
||||
passwordHash: text(),
|
||||
displayName: text(),
|
||||
});
|
||||
|
||||
export const sessions = pgTable("sessions", {
|
||||
id: integer().primaryKey().generatedAlwaysAsIdentity(),
|
||||
userId: integer().references(() => users.id),
|
||||
expiresAt: timestamp({withTimezone: true}).notNull(),
|
||||
id: text().primaryKey(),
|
||||
userId: integer()
|
||||
.notNull()
|
||||
.references(() => users.id),
|
||||
expiresAt: timestamp({ withTimezone: true }).notNull(),
|
||||
});
|
||||
|
||||
export const sessionRelations = relations(sessions, ({ one }) => ({
|
||||
user: one(users, {
|
||||
fields: [ sessions.userId ],
|
||||
references: [users.id],
|
||||
}),
|
||||
}));
|
||||
|
@ -1,5 +1,123 @@
|
||||
import {
|
||||
encodeBase32LowerCaseNoPadding,
|
||||
encodeHexLowerCase,
|
||||
} from "@oslojs/encoding";
|
||||
import { sha256 } from "@oslojs/crypto/sha2";
|
||||
import type { RequestEvent } from "@sveltejs/kit";
|
||||
import { eq } from "drizzle-orm";
|
||||
import { Temporal, toTemporalInstant } from "@js-temporal/polyfill";
|
||||
|
||||
import { db } from "./db";
|
||||
import { users, sessions } from "./schema";
|
||||
import type { User } from "./user";
|
||||
|
||||
const sessionExpirySeconds = 30 * 86400;
|
||||
const sessionExpiry = new Temporal.Duration(0, 0, 0, 0, 0, 0, sessionExpirySeconds);
|
||||
const sessionRenewal = new Temporal.Duration(0, 0, 0, 0, 0, 0, sessionExpirySeconds/2);
|
||||
|
||||
export async function validateSessionToken(
|
||||
token: string,
|
||||
): Promise<SessionValidationResult> {
|
||||
const sessionId = encodeHexLowerCase(sha256(new TextEncoder().encode(token)));
|
||||
|
||||
const row = await db.query.sessions.findFirst({
|
||||
with: {
|
||||
user: true,
|
||||
},
|
||||
where: (sessions, { eq }) => eq(sessions.id, sessionId),
|
||||
});
|
||||
|
||||
if (!row) {
|
||||
return { session: null, user: null };
|
||||
}
|
||||
const session: Session = {
|
||||
id: row.id,
|
||||
userId: row.userId,
|
||||
expiresAt: row.expiresAt,
|
||||
};
|
||||
|
||||
const user: User = {
|
||||
id: row.userId,
|
||||
username: row.user.username,
|
||||
displayName: row.user.displayName || undefined,
|
||||
};
|
||||
|
||||
const now = Temporal.Now.instant();
|
||||
const expiresAt = toTemporalInstant.apply(session.expiresAt);
|
||||
|
||||
if (Temporal.Instant.compare(now, expiresAt) > 0) {
|
||||
await db.delete(sessions).where(eq(sessions.id, session.id));
|
||||
return { session: null, user: null };
|
||||
}
|
||||
|
||||
if (Temporal.Instant.compare(now.add(sessionRenewal), expiresAt) > 0) {
|
||||
session.expiresAt = new Date(now.add(sessionExpiry).epochMilliseconds);
|
||||
await db.update(sessions)
|
||||
.set({ expiresAt: session.expiresAt })
|
||||
.where(eq(sessions.id, session.id));
|
||||
}
|
||||
|
||||
return { session, user };
|
||||
}
|
||||
|
||||
export async function invalidateSession(sessionId: string): Promise<void> {
|
||||
await db.delete(sessions).where(eq(sessions.id, sessionId));
|
||||
}
|
||||
|
||||
export async function invalidateUserSessions(userId: number): Promise<void> {
|
||||
await db.delete(sessions).where(eq(sessions.userId, userId));
|
||||
}
|
||||
|
||||
export function setSessionTokenCookie(
|
||||
event: RequestEvent,
|
||||
token: string,
|
||||
expiresAt: Date,
|
||||
): void {
|
||||
event.cookies.set("session", token, {
|
||||
httpOnly: true,
|
||||
path: "/",
|
||||
secure: import.meta.env.PROD,
|
||||
sameSite: "lax",
|
||||
expires: expiresAt,
|
||||
});
|
||||
}
|
||||
|
||||
export function deleteSessionTokenCookie(event: RequestEvent): void {
|
||||
event.cookies.set("session", "", {
|
||||
httpOnly: true,
|
||||
path: "/",
|
||||
secure: import.meta.env.PROD,
|
||||
sameSite: "lax",
|
||||
maxAge: 0,
|
||||
});
|
||||
}
|
||||
|
||||
export function generateSessionToken(): string {
|
||||
const tokenBytes = new Uint8Array(20);
|
||||
crypto.getRandomValues(tokenBytes);
|
||||
const token = encodeBase32LowerCaseNoPadding(tokenBytes).toLowerCase();
|
||||
return token;
|
||||
}
|
||||
|
||||
export async function createSession(token: string, userId: number): Promise<Session> {
|
||||
const sessionId = encodeHexLowerCase(sha256(new TextEncoder().encode(token)));
|
||||
const session: Session = {
|
||||
id: sessionId,
|
||||
userId,
|
||||
expiresAt: new Date(
|
||||
Temporal.Now.instant().add(sessionExpiry).epochMilliseconds,
|
||||
),
|
||||
};
|
||||
await db.insert(sessions).values(session);
|
||||
return session;
|
||||
}
|
||||
|
||||
export interface Session {
|
||||
id: string;
|
||||
expiresAt: Date;
|
||||
userId: number;
|
||||
}
|
||||
|
||||
type SessionValidationResult =
|
||||
| { session: Session; user: User }
|
||||
| { session: null; user: null };
|
||||
|
@ -1,5 +1,45 @@
|
||||
import { eq } from "drizzle-orm";
|
||||
import { db } from "./db";
|
||||
import { users } from "./schema";
|
||||
|
||||
export interface User {
|
||||
id: number;
|
||||
username: string;
|
||||
displayName: string;
|
||||
displayName?: string;
|
||||
}
|
||||
|
||||
|
||||
export async function getUserFromUsername(username: string): Promise<User | null> {
|
||||
const user = await db.query.users.findFirst({
|
||||
columns: {
|
||||
id: true,
|
||||
username: true,
|
||||
displayName: true
|
||||
},
|
||||
where: eq(users.username, username)
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
...user,
|
||||
displayName: user.displayName || undefined,
|
||||
};
|
||||
}
|
||||
|
||||
export async function getUserPasswordHash(userId: number): Promise<string|null> {
|
||||
const user = (await db.query.users.findFirst({
|
||||
columns: {
|
||||
passwordHash: true,
|
||||
},
|
||||
where: eq(users.id, userId)
|
||||
}));
|
||||
|
||||
if (user === undefined) {
|
||||
throw new Error("Invalid user ID");
|
||||
}
|
||||
|
||||
return user.passwordHash;
|
||||
}
|
7
src/routes/+layout.server.ts
Normal file
7
src/routes/+layout.server.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import type { LayoutServerLoad } from "./$types";
|
||||
|
||||
export const load: LayoutServerLoad = async (event) => {
|
||||
return {
|
||||
username: event.locals.user?.displayName || event.locals.user?.username,
|
||||
};
|
||||
};
|
16
src/routes/+layout.svelte
Normal file
16
src/routes/+layout.svelte
Normal file
@ -0,0 +1,16 @@
|
||||
<script lang="ts">
|
||||
const { data, children } = $props();
|
||||
</script>
|
||||
|
||||
<nav>
|
||||
{#if data.username}
|
||||
<div>
|
||||
<p>Logged in as {data.username}.</p>
|
||||
<form method="POST" action="/logout">
|
||||
<button>Log out</button>
|
||||
</form>
|
||||
</div>
|
||||
{/if}
|
||||
</nav>
|
||||
|
||||
{@render children()}
|
@ -1,12 +0,0 @@
|
||||
import { count } from "drizzle-orm";
|
||||
|
||||
import { db } from "$lib/server/db";
|
||||
import { users } from "$lib/server/schema";
|
||||
|
||||
import type { PageServerLoad } from "./$types";
|
||||
|
||||
export const load: PageServerLoad = async () => {
|
||||
return {
|
||||
c: (await db.select({ value: count() }).from(users))[0]
|
||||
};
|
||||
};
|
@ -6,5 +6,3 @@
|
||||
|
||||
<h1>Welcome to SvelteKit</h1>
|
||||
<p>Visit <a href="https://svelte.dev/docs/kit">svelte.dev/docs/kit</a> to read the documentation</p>
|
||||
|
||||
<p>There is { data.c.value } users!</p>
|
@ -1,18 +1,87 @@
|
||||
import { redirect } from "@sveltejs/kit";
|
||||
import { RefillingTokenBucket, Throttler } from "$lib/server/rate-limit";
|
||||
import { fail, redirect } from "@sveltejs/kit";
|
||||
|
||||
import type { Actions, PageServerLoad, PageServerLoadEvent } from "./$types";
|
||||
|
||||
import { RefillingTokenBucket, Throttler } from "$lib/server/rate-limit";
|
||||
import type { Actions, PageServerLoad, PageServerLoadEvent, RequestEvent } from "./$types";
|
||||
import { getUserFromUsername, getUserPasswordHash } from "$lib/server/user";
|
||||
import { verifyPasswordHash } from "$lib/server/password";
|
||||
import { createSession, generateSessionToken, setSessionTokenCookie } from "$lib/server/session";
|
||||
|
||||
const throttler = new Throttler<number>([0, 1, 2, 4, 8, 16, 30, 60, 180, 300]);
|
||||
const ipBucket = new RefillingTokenBucket<string>(20, 1);
|
||||
|
||||
export const load: PageServerLoad = (event: PageServerLoadEvent) => {
|
||||
if (event.locals.session !== null && event.locals.user !== null) {
|
||||
return redirect(302, "/");
|
||||
redirect(303, "/");
|
||||
}
|
||||
return {};
|
||||
};
|
||||
|
||||
export const actions = {
|
||||
default: async (event) => {},
|
||||
} satisfies Actions;
|
||||
export const actions: Actions = {
|
||||
default: action
|
||||
};
|
||||
|
||||
async function action(event: RequestEvent) {
|
||||
// TODO: Assumes X-Forwarded-For is always included.
|
||||
const clientIP = event.request.headers.get("X-Forwarded-For");
|
||||
if (clientIP !== null && !ipBucket.check(clientIP, 1)) {
|
||||
return fail(429, {
|
||||
message: "Too many requests",
|
||||
email: ""
|
||||
});
|
||||
}
|
||||
|
||||
const formData = await event.request.formData();
|
||||
const username = formData.get("username");
|
||||
const password = formData.get("password");
|
||||
if (typeof username !== "string" || typeof password !== "string") {
|
||||
return fail(400, {
|
||||
message: "Invalid or missing fields",
|
||||
username: ""
|
||||
});
|
||||
}
|
||||
if (username === "" || password === "") {
|
||||
return fail(400, {
|
||||
message: "Please enter your username and password.",
|
||||
username: username
|
||||
});
|
||||
}
|
||||
const user = await getUserFromUsername(username);
|
||||
if (user === null) {
|
||||
return fail(400, {
|
||||
message: "Account does not exist",
|
||||
username: username
|
||||
});
|
||||
}
|
||||
if (clientIP !== null && !ipBucket.consume(clientIP, 1)) {
|
||||
return fail(429, {
|
||||
message: "Too many requests",
|
||||
username: ""
|
||||
});
|
||||
}
|
||||
if (!throttler.consume(user.id)) {
|
||||
return fail(429, {
|
||||
message: "Too many requests",
|
||||
username: ""
|
||||
});
|
||||
}
|
||||
const passwordHash = await getUserPasswordHash(user.id);
|
||||
if (passwordHash === null) {
|
||||
return fail(400, {
|
||||
message: "Account locked",
|
||||
username
|
||||
})
|
||||
}
|
||||
const validPassword = await verifyPasswordHash(passwordHash, password);
|
||||
if (!validPassword) {
|
||||
return fail(400, {
|
||||
message: "Invalid password",
|
||||
username
|
||||
});
|
||||
}
|
||||
throttler.reset(user.id);
|
||||
const sessionToken = generateSessionToken();
|
||||
const session = await createSession(sessionToken, user.id);
|
||||
setSessionTokenCookie(event, sessionToken, session.expiresAt);
|
||||
return redirect(303, "/");
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
<script lang="ts">
|
||||
import { enhance } from "$app/forms";
|
||||
|
||||
import type { PageProps } from "./$types";
|
||||
|
||||
const { data, form }: PageProps = $props();
|
||||
</script>
|
||||
<form method="post" use:enhance>
|
||||
<label for="form-login.username">Username</label>
|
||||
<input
|
||||
id="form-login.username"
|
||||
name="username"
|
||||
autocomplete="username"
|
||||
required
|
||||
value={form?.username || ""}
|
||||
/><br />
|
||||
<label for="form-login.password">Password</label>
|
||||
<input
|
||||
type="password"
|
||||
id="form-login.password"
|
||||
name="password"
|
||||
autocomplete="current-password"
|
||||
required
|
||||
/><br />
|
||||
<button>Continue</button>
|
||||
<p>{form?.message ?? ""}</p>
|
||||
</form>
|
28
src/routes/logout/+page.server.ts
Normal file
28
src/routes/logout/+page.server.ts
Normal file
@ -0,0 +1,28 @@
|
||||
import { fail, redirect, type Actions } from "@sveltejs/kit";
|
||||
|
||||
import type { PageServerLoad } from "./$types";
|
||||
import {
|
||||
deleteSessionTokenCookie,
|
||||
invalidateSession,
|
||||
} from "$lib/server/session";
|
||||
|
||||
export const load: PageServerLoad = (event) => {
|
||||
if (event.locals.session !== null && event.locals.user !== null) {
|
||||
return redirect(302, "/");
|
||||
}
|
||||
return {};
|
||||
};
|
||||
|
||||
export const actions = {
|
||||
default: async (event) => {
|
||||
if (event.locals.session === null) {
|
||||
return fail(401, {
|
||||
message: "Not authenticated",
|
||||
});
|
||||
}
|
||||
|
||||
await invalidateSession(event.locals.session.id);
|
||||
deleteSessionTokenCookie(event);
|
||||
return redirect(303, "/login");
|
||||
},
|
||||
} satisfies Actions;
|
@ -1,4 +1,4 @@
|
||||
import adapter from "@sveltejs/adapter-auto";
|
||||
import adapter from "@sveltejs/adapter-node";
|
||||
import { vitePreprocess } from "@sveltejs/vite-plugin-svelte";
|
||||
|
||||
/** @type {import('@sveltejs/kit').Config} */
|
||||
@ -13,6 +13,9 @@ const config = {
|
||||
// See https://svelte.dev/docs/kit/adapters for more information about adapters.
|
||||
adapter: adapter(),
|
||||
},
|
||||
compilerOptions: {
|
||||
runes: true,
|
||||
}
|
||||
};
|
||||
|
||||
export default config;
|
||||
|
@ -6,5 +6,5 @@ export default defineConfig({
|
||||
|
||||
test: {
|
||||
include: ["src/**/*.{test,spec}.{js,ts}"],
|
||||
},
|
||||
}
|
||||
});
|
||||
|
Loading…
x
Reference in New Issue
Block a user