From 372db5921119e2a690418bffa535eba00cfd4cc1 Mon Sep 17 00:00:00 2001 From: Sean Morley Date: Wed, 3 Apr 2024 00:51:12 +0000 Subject: [PATCH 01/28] Added auth! --- migrations/0004_smart_maelstrom.sql | 15 + migrations/0005_glamorous_pixie.sql | 2 + migrations/0006_melted_leech.sql | 1 + migrations/meta/0004_snapshot.json | 123 +++ migrations/meta/0005_snapshot.json | 135 +++ migrations/meta/0006_snapshot.json | 135 +++ migrations/meta/_journal.json | 21 + package-lock.json | 1193 ++++++++++++++++++++++++++- package.json | 6 +- src/app.d.ts | 11 +- src/hooks.server.ts | 32 + src/lib/db/db.server.ts | 5 +- src/lib/db/schema.ts | 28 +- src/lib/server/auth.ts | 38 + src/routes/login/+page.server.ts | 76 ++ src/routes/login/+page.svelte | 13 + src/routes/signup/+page.server.ts | 60 ++ src/routes/signup/+page.svelte | 13 + 18 files changed, 1887 insertions(+), 20 deletions(-) create mode 100644 migrations/0004_smart_maelstrom.sql create mode 100644 migrations/0005_glamorous_pixie.sql create mode 100644 migrations/0006_melted_leech.sql create mode 100644 migrations/meta/0004_snapshot.json create mode 100644 migrations/meta/0005_snapshot.json create mode 100644 migrations/meta/0006_snapshot.json create mode 100644 src/hooks.server.ts create mode 100644 src/lib/server/auth.ts create mode 100644 src/routes/login/+page.server.ts create mode 100644 src/routes/login/+page.svelte create mode 100644 src/routes/signup/+page.server.ts create mode 100644 src/routes/signup/+page.svelte diff --git a/migrations/0004_smart_maelstrom.sql b/migrations/0004_smart_maelstrom.sql new file mode 100644 index 0000000..5982edc --- /dev/null +++ b/migrations/0004_smart_maelstrom.sql @@ -0,0 +1,15 @@ +CREATE TABLE IF NOT EXISTS "session" ( + "id" text PRIMARY KEY NOT NULL, + "user_id" text NOT NULL, + "expires_at" timestamp with time zone NOT NULL +); +--> statement-breakpoint +CREATE TABLE IF NOT EXISTS "user" ( + "id" text PRIMARY KEY NOT NULL +); +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "session" ADD CONSTRAINT "session_user_id_user_id_fk" FOREIGN KEY ("user_id") REFERENCES "user"("id") ON DELETE no action ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; diff --git a/migrations/0005_glamorous_pixie.sql b/migrations/0005_glamorous_pixie.sql new file mode 100644 index 0000000..c50335d --- /dev/null +++ b/migrations/0005_glamorous_pixie.sql @@ -0,0 +1,2 @@ +ALTER TABLE "user" ADD COLUMN "username" text NOT NULL;--> statement-breakpoint +ALTER TABLE "user" ADD COLUMN "hashed_password" text NOT NULL; \ No newline at end of file diff --git a/migrations/0006_melted_leech.sql b/migrations/0006_melted_leech.sql new file mode 100644 index 0000000..202d31b --- /dev/null +++ b/migrations/0006_melted_leech.sql @@ -0,0 +1 @@ +ALTER TABLE "user" ALTER COLUMN "hashed_password" SET DATA TYPE varchar; \ No newline at end of file diff --git a/migrations/meta/0004_snapshot.json b/migrations/meta/0004_snapshot.json new file mode 100644 index 0000000..9d59b97 --- /dev/null +++ b/migrations/meta/0004_snapshot.json @@ -0,0 +1,123 @@ +{ + "id": "ccf7c336-c61f-452f-822b-b3b039bb20f9", + "prevId": "45d98527-f0a9-44fc-9658-d3c461afed95", + "version": "5", + "dialect": "pg", + "tables": { + "featuredAdventures": { + "name": "featuredAdventures", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "location": { + "name": "location", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "session": { + "name": "session", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "session_user_id_user_id_fk": { + "name": "session_user_id_user_id_fk", + "tableFrom": "session", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "sharedAdventures": { + "name": "sharedAdventures", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "data": { + "name": "data", + "type": "json", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "user": { + "name": "user", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + } + }, + "enums": {}, + "schemas": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/migrations/meta/0005_snapshot.json b/migrations/meta/0005_snapshot.json new file mode 100644 index 0000000..116f836 --- /dev/null +++ b/migrations/meta/0005_snapshot.json @@ -0,0 +1,135 @@ +{ + "id": "e91dda33-e04e-4e99-a297-21a34aa35493", + "prevId": "ccf7c336-c61f-452f-822b-b3b039bb20f9", + "version": "5", + "dialect": "pg", + "tables": { + "featuredAdventures": { + "name": "featuredAdventures", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "location": { + "name": "location", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "session": { + "name": "session", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "session_user_id_user_id_fk": { + "name": "session_user_id_user_id_fk", + "tableFrom": "session", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "sharedAdventures": { + "name": "sharedAdventures", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "data": { + "name": "data", + "type": "json", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "user": { + "name": "user", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "hashed_password": { + "name": "hashed_password", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + } + }, + "enums": {}, + "schemas": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/migrations/meta/0006_snapshot.json b/migrations/meta/0006_snapshot.json new file mode 100644 index 0000000..2b36a76 --- /dev/null +++ b/migrations/meta/0006_snapshot.json @@ -0,0 +1,135 @@ +{ + "id": "b0849b3e-02e1-42e1-b07c-6fa613c98e82", + "prevId": "e91dda33-e04e-4e99-a297-21a34aa35493", + "version": "5", + "dialect": "pg", + "tables": { + "featuredAdventures": { + "name": "featuredAdventures", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "location": { + "name": "location", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "session": { + "name": "session", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "session_user_id_user_id_fk": { + "name": "session_user_id_user_id_fk", + "tableFrom": "session", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "sharedAdventures": { + "name": "sharedAdventures", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "data": { + "name": "data", + "type": "json", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "user": { + "name": "user", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "hashed_password": { + "name": "hashed_password", + "type": "varchar", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + } + }, + "enums": {}, + "schemas": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/migrations/meta/_journal.json b/migrations/meta/_journal.json index 5af8112..ff1ddfe 100644 --- a/migrations/meta/_journal.json +++ b/migrations/meta/_journal.json @@ -29,6 +29,27 @@ "when": 1712083977580, "tag": "0003_clammy_goblin_queen", "breakpoints": true + }, + { + "idx": 4, + "version": "5", + "when": 1712103855532, + "tag": "0004_smart_maelstrom", + "breakpoints": true + }, + { + "idx": 5, + "version": "5", + "when": 1712104331399, + "tag": "0005_glamorous_pixie", + "breakpoints": true + }, + { + "idx": 6, + "version": "5", + "when": 1712105206127, + "tag": "0006_melted_leech", + "breakpoints": true } ] } \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index e084878..caa4707 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,7 +8,9 @@ "name": "adventurelog", "version": "0.0.1", "dependencies": { + "@lucia-auth/adapter-drizzle": "^1.0.7", "drizzle-orm": "^0.30.6", + "oslo": "^1.2.0", "postgres": "^3.4.4" }, "devDependencies": { @@ -18,11 +20,13 @@ "@sveltejs/kit": "^2.0.0", "@sveltejs/vite-plugin-svelte": "^3.0.0", "@tailwindcss/typography": "^0.5.12", + "@types/pg": "^8.11.4", "autoprefixer": "^10.4.19", "daisyui": "^4.9.0", "dotenv": "^16.4.5", "drizzle-kit": "^0.20.14", - "pg": "^8.11.4", + "lucia": "^3.1.1", + "pg": "^8.11.5", "postcss": "^8.4.38", "svelte": "^4.2.7", "svelte-check": "^3.6.0", @@ -66,6 +70,24 @@ "superjson": "^2.2.1" } }, + "node_modules/@emnapi/core": { + "version": "0.45.0", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-0.45.0.tgz", + "integrity": "sha512-DPWjcUDQkCeEM4VnljEOEcXdAD7pp8zSZsgOujk/LGIwCXWbXJngin+MO4zbH429lzeC3WbYLGjE2MaUOwzpyw==", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "0.45.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-0.45.0.tgz", + "integrity": "sha512-Txumi3td7J4A/xTTwlssKieHKTGl3j4A1tglBx72auZ49YK7ePY6XZricgIg9mnZT4xPfA+UPCUdnhRuEFDL+w==", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@esbuild-kit/core-utils": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/@esbuild-kit/core-utils/-/core-utils-3.3.2.tgz", @@ -908,6 +930,14 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@lucia-auth/adapter-drizzle": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@lucia-auth/adapter-drizzle/-/adapter-drizzle-1.0.7.tgz", + "integrity": "sha512-X/V7fLBca8EC/gPXCntwbQpb0+F9oEuRoHElvsi9rCrdnGhCMNxHgwAvgiQ6pes+rIYpyvx4n3hvjqo/fPo03A==", + "peerDependencies": { + "lucia": "3.x" + } + }, "node_modules/@mapbox/node-pre-gyp": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz", @@ -943,6 +973,513 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.1.2.tgz", + "integrity": "sha512-8JuczewTFIZ/XIjHQ+YlQUydHvlKx2hkcxtuGwh+t/t5zWyZct6YG4+xjHcq8xyc/e7FmFwf42Zj2YgICwmlvA==", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.1.0", + "@emnapi/runtime": "^1.1.0", + "@tybys/wasm-util": "^0.8.1" + } + }, + "node_modules/@napi-rs/wasm-runtime/node_modules/@emnapi/core": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.1.1.tgz", + "integrity": "sha512-eu4KjHfXg3I+UUR7vSuwZXpRo4c8h4Rtb5Lu2F7Z4JqJFl/eidquONEBiRs6viXKpWBC3BaJBy68xGJ2j56idw==", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@napi-rs/wasm-runtime/node_modules/@emnapi/runtime": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.1.1.tgz", + "integrity": "sha512-3bfqkzuR1KLx57nZfjr2NLnFOobvyS0aTszaEGCGqmYMVDRaGvgIZbjGSV/MHSSmLgQ/b9JFHQ5xm5WRZYd+XQ==", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@node-rs/argon2": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@node-rs/argon2/-/argon2-1.7.0.tgz", + "integrity": "sha512-zfULc+/tmcWcxn+nHkbyY8vP3+MpEqKORbszt4UkpqZgBgDAAIYvuDN/zukfTgdmo6tmJKKVfzigZOPk4LlIog==", + "engines": { + "node": ">= 10" + }, + "optionalDependencies": { + "@node-rs/argon2-android-arm-eabi": "1.7.0", + "@node-rs/argon2-android-arm64": "1.7.0", + "@node-rs/argon2-darwin-arm64": "1.7.0", + "@node-rs/argon2-darwin-x64": "1.7.0", + "@node-rs/argon2-freebsd-x64": "1.7.0", + "@node-rs/argon2-linux-arm-gnueabihf": "1.7.0", + "@node-rs/argon2-linux-arm64-gnu": "1.7.0", + "@node-rs/argon2-linux-arm64-musl": "1.7.0", + "@node-rs/argon2-linux-x64-gnu": "1.7.0", + "@node-rs/argon2-linux-x64-musl": "1.7.0", + "@node-rs/argon2-wasm32-wasi": "1.7.0", + "@node-rs/argon2-win32-arm64-msvc": "1.7.0", + "@node-rs/argon2-win32-ia32-msvc": "1.7.0", + "@node-rs/argon2-win32-x64-msvc": "1.7.0" + } + }, + "node_modules/@node-rs/argon2-android-arm-eabi": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-android-arm-eabi/-/argon2-android-arm-eabi-1.7.0.tgz", + "integrity": "sha512-udDqkr5P9E+wYX1SZwAVPdyfYvaF4ry9Tm+R9LkfSHbzWH0uhU6zjIwNRp7m+n4gx691rk+lqqDAIP8RLKwbhg==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/argon2-android-arm64": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-android-arm64/-/argon2-android-arm64-1.7.0.tgz", + "integrity": "sha512-s9j/G30xKUx8WU50WIhF0fIl1EdhBGq0RQ06lEhZ0Gi0ap8lhqbE2Bn5h3/G2D1k0Dx+yjeVVNmt/xOQIRG38A==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/argon2-darwin-arm64": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-darwin-arm64/-/argon2-darwin-arm64-1.7.0.tgz", + "integrity": "sha512-ZIz4L6HGOB9U1kW23g+m7anGNuTZ0RuTw0vNp3o+2DWpb8u8rODq6A8tH4JRL79S+Co/Nq608m9uackN2pe0Rw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/argon2-darwin-x64": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-darwin-x64/-/argon2-darwin-x64-1.7.0.tgz", + "integrity": "sha512-5oi/pxqVhODW/pj1+3zElMTn/YukQeywPHHYDbcAW3KsojFjKySfhcJMd1DjKTc+CHQI+4lOxZzSUzK7mI14Hw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/argon2-freebsd-x64": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-freebsd-x64/-/argon2-freebsd-x64-1.7.0.tgz", + "integrity": "sha512-Ify08683hA4QVXYoIm5SUWOY5DPIT/CMB0CQT+IdxQAg/F+qp342+lUkeAtD5bvStQuCx/dFO3bnnzoe2clMhA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/argon2-linux-arm-gnueabihf": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-arm-gnueabihf/-/argon2-linux-arm-gnueabihf-1.7.0.tgz", + "integrity": "sha512-7DjDZ1h5AUHAtRNjD19RnQatbhL+uuxBASuuXIBu4/w6Dx8n7YPxwTP4MXfsvuRgKuMWiOb/Ub/HJ3kXVCXRkg==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/argon2-linux-arm64-gnu": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-arm64-gnu/-/argon2-linux-arm64-gnu-1.7.0.tgz", + "integrity": "sha512-nJDoMP4Y3YcqGswE4DvP080w6O24RmnFEDnL0emdI8Nou17kNYBzP2546Nasx9GCyLzRcYQwZOUjrtUuQ+od2g==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/argon2-linux-arm64-musl": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-arm64-musl/-/argon2-linux-arm64-musl-1.7.0.tgz", + "integrity": "sha512-BKWS8iVconhE3jrb9mj6t1J9vwUqQPpzCbUKxfTGJfc+kNL58F1SXHBoe2cDYGnHrFEHTY0YochzXoAfm4Dm/A==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/argon2-linux-x64-gnu": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-x64-gnu/-/argon2-linux-x64-gnu-1.7.0.tgz", + "integrity": "sha512-EmgqZOlf4Jurk/szW1iTsVISx25bKksVC5uttJDUloTgsAgIGReCpUUO1R24pBhu9ESJa47iv8NSf3yAfGv6jQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/argon2-linux-x64-musl": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-x64-musl/-/argon2-linux-x64-musl-1.7.0.tgz", + "integrity": "sha512-/o1efYCYIxjfuoRYyBTi2Iy+1iFfhqHCvvVsnjNSgO1xWiWrX0Rrt/xXW5Zsl7vS2Y+yu8PL8KFWRzZhaVxfKA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/argon2-wasm32-wasi": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-wasm32-wasi/-/argon2-wasm32-wasi-1.7.0.tgz", + "integrity": "sha512-Evmk9VcxqnuwQftfAfYEr6YZYSPLzmKUsbFIMep5nTt9PT4XYRFAERj7wNYp+rOcBenF3X4xoB+LhwcOMTNE5w==", + "cpu": [ + "wasm32" + ], + "optional": true, + "dependencies": { + "@emnapi/core": "^0.45.0", + "@emnapi/runtime": "^0.45.0", + "@tybys/wasm-util": "^0.8.1", + "memfs-browser": "^3.4.13000" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@node-rs/argon2-win32-arm64-msvc": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-win32-arm64-msvc/-/argon2-win32-arm64-msvc-1.7.0.tgz", + "integrity": "sha512-qgsU7T004COWWpSA0tppDqDxbPLgg8FaU09krIJ7FBl71Sz8SFO40h7fDIjfbTT5w7u6mcaINMQ5bSHu75PCaA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/argon2-win32-ia32-msvc": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-win32-ia32-msvc/-/argon2-win32-ia32-msvc-1.7.0.tgz", + "integrity": "sha512-JGafwWYQ/HpZ3XSwP4adQ6W41pRvhcdXvpzIWtKvX+17+xEXAe2nmGWM6s27pVkg1iV2ZtoYLRDkOUoGqZkCcg==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/argon2-win32-x64-msvc": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-win32-x64-msvc/-/argon2-win32-x64-msvc-1.7.0.tgz", + "integrity": "sha512-9oq4ShyFakw8AG3mRls0AoCpxBFcimYx7+jvXeAf2OqKNO+mSA6eZ9z7KQeVCi0+SOEUYxMGf5UiGiDb9R6+9Q==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/bcrypt": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt/-/bcrypt-1.9.0.tgz", + "integrity": "sha512-u2OlIxW264bFUfvbFqDz9HZKFjwe8FHFtn7T/U8mYjPZ7DWYpbUB+/dkW/QgYfMSfR0ejkyuWaBBe0coW7/7ig==", + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "optionalDependencies": { + "@node-rs/bcrypt-android-arm-eabi": "1.9.0", + "@node-rs/bcrypt-android-arm64": "1.9.0", + "@node-rs/bcrypt-darwin-arm64": "1.9.0", + "@node-rs/bcrypt-darwin-x64": "1.9.0", + "@node-rs/bcrypt-freebsd-x64": "1.9.0", + "@node-rs/bcrypt-linux-arm-gnueabihf": "1.9.0", + "@node-rs/bcrypt-linux-arm64-gnu": "1.9.0", + "@node-rs/bcrypt-linux-arm64-musl": "1.9.0", + "@node-rs/bcrypt-linux-x64-gnu": "1.9.0", + "@node-rs/bcrypt-linux-x64-musl": "1.9.0", + "@node-rs/bcrypt-wasm32-wasi": "1.9.0", + "@node-rs/bcrypt-win32-arm64-msvc": "1.9.0", + "@node-rs/bcrypt-win32-ia32-msvc": "1.9.0", + "@node-rs/bcrypt-win32-x64-msvc": "1.9.0" + } + }, + "node_modules/@node-rs/bcrypt-android-arm-eabi": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-android-arm-eabi/-/bcrypt-android-arm-eabi-1.9.0.tgz", + "integrity": "sha512-nOCFISGtnodGHNiLrG0WYLWr81qQzZKYfmwHc7muUeq+KY0sQXyHOwZk9OuNQAWv/lnntmtbwkwT0QNEmOyLvA==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/bcrypt-android-arm64": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-android-arm64/-/bcrypt-android-arm64-1.9.0.tgz", + "integrity": "sha512-+ZrIAtigVmjYkqZQTThHVlz0+TG6D+GDHWhVKvR2DifjtqJ0i+mb9gjo++hN+fWEQdWNGxKCiBBjwgT4EcXd6A==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/bcrypt-darwin-arm64": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-darwin-arm64/-/bcrypt-darwin-arm64-1.9.0.tgz", + "integrity": "sha512-CQiS+F9Pa0XozvkXR1g7uXE9QvBOPOplDg0iCCPRYTN9PqA5qYxhwe48G3o+v2UeQceNRrbnEtWuANm7JRqIhw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/bcrypt-darwin-x64": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-darwin-x64/-/bcrypt-darwin-x64-1.9.0.tgz", + "integrity": "sha512-4pTKGawYd7sNEjdJ7R/R67uwQH1VvwPZ0SSUMmeNHbxD5QlwAPXdDH11q22uzVXsvNFZ6nGQBg8No5OUGpx6Ug==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/bcrypt-freebsd-x64": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-freebsd-x64/-/bcrypt-freebsd-x64-1.9.0.tgz", + "integrity": "sha512-UmWzySX4BJhT/B8xmTru6iFif3h0Rpx3TqxRLCcbgmH43r7k5/9QuhpiyzpvKGpKHJCFNm4F3rC2wghvw5FCIg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/bcrypt-linux-arm-gnueabihf": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-linux-arm-gnueabihf/-/bcrypt-linux-arm-gnueabihf-1.9.0.tgz", + "integrity": "sha512-8qoX4PgBND2cVwsbajoAWo3NwdfJPEXgpCsZQZURz42oMjbGyhhSYbovBCskGU3EBLoC8RA2B1jFWooeYVn5BA==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/bcrypt-linux-arm64-gnu": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-linux-arm64-gnu/-/bcrypt-linux-arm64-gnu-1.9.0.tgz", + "integrity": "sha512-TuAC6kx0SbcIA4mSEWPi+OCcDjTQUMl213v5gMNlttF+D4ieIZx6pPDGTaMO6M2PDHTeCG0CBzZl0Lu+9b0c7Q==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/bcrypt-linux-arm64-musl": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-linux-arm64-musl/-/bcrypt-linux-arm64-musl-1.9.0.tgz", + "integrity": "sha512-/sIvKDABOI8QOEnLD7hIj02BVaNOuCIWBKvxcJOt8+TuwJ6zmY1UI5kSv9d99WbiHjTp97wtAUbZQwauU4b9ew==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/bcrypt-linux-x64-gnu": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-linux-x64-gnu/-/bcrypt-linux-x64-gnu-1.9.0.tgz", + "integrity": "sha512-DyyhDHDsLBsCKz1tZ1hLvUZSc1DK0FU0v52jK6IBQxrj24WscSU9zZe7ie/V9kdmA4Ep57BfpWX8Dsa2JxGdgQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/bcrypt-linux-x64-musl": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-linux-x64-musl/-/bcrypt-linux-x64-musl-1.9.0.tgz", + "integrity": "sha512-duIiuqQ+Lew8ASSAYm6ZRqcmfBGWwsi81XLUwz86a2HR7Qv6V4yc3ZAUQovAikhjCsIqe8C11JlAZSK6+PlXYg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/bcrypt-wasm32-wasi": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-wasm32-wasi/-/bcrypt-wasm32-wasi-1.9.0.tgz", + "integrity": "sha512-ylaGmn9Wjwv/D5lxtawttx3H6Uu2WTTR7lWlRHGT6Ga/MB1Vj4OjSGUW8G8zIVnKuXpGbZ92pgHlt4HUpSLctw==", + "cpu": [ + "wasm32" + ], + "optional": true, + "dependencies": { + "@emnapi/core": "^0.45.0", + "@emnapi/runtime": "^0.45.0", + "@tybys/wasm-util": "^0.8.1", + "memfs-browser": "^3.4.13000" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@node-rs/bcrypt-win32-arm64-msvc": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-win32-arm64-msvc/-/bcrypt-win32-arm64-msvc-1.9.0.tgz", + "integrity": "sha512-2h86gF7QFyEzODuDFml/Dp1MSJoZjxJ4yyT2Erf4NkwsiA5MqowUhUsorRwZhX6+2CtlGa7orbwi13AKMsYndw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/bcrypt-win32-ia32-msvc": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-win32-ia32-msvc/-/bcrypt-win32-ia32-msvc-1.9.0.tgz", + "integrity": "sha512-kqxalCvhs4FkN0+gWWfa4Bdy2NQAkfiqq/CEf6mNXC13RSV673Ev9V8sRlQyNpCHCNkeXfOT9pgoBdJmMs9muA==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/bcrypt-win32-x64-msvc": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-win32-x64-msvc/-/bcrypt-win32-x64-msvc-1.9.0.tgz", + "integrity": "sha512-2y0Tuo6ZAT2Cz8V7DHulSlv1Bip3zbzeXyeur+uR25IRNYXKvI/P99Zl85Fbuu/zzYAZRLLlGTRe6/9IHofe/w==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -1887,6 +2424,15 @@ "node": ">=4" } }, + "node_modules/@tybys/wasm-util": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.8.1.tgz", + "integrity": "sha512-GSsTwyBl4pIzsxAY5wroZdyQKyhXk0d8PCRZtrSZ2WEB1cBdrp2EgGBwHOGCZtIIPun/DL3+AykCv+J6fyRH4Q==", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@types/cookie": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", @@ -1899,6 +2445,83 @@ "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", "dev": true }, + "node_modules/@types/node": { + "version": "20.12.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.3.tgz", + "integrity": "sha512-sD+ia2ubTeWrOu+YMF+MTAB7E+O7qsMqAbMfW7DG3K1URwhZ5hN1pLlRVGbf4wDFzSfikL05M17EyorS86jShw==", + "devOptional": true, + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/pg": { + "version": "8.11.4", + "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.11.4.tgz", + "integrity": "sha512-yw3Bwbda6vO+NvI1Ue/YKOwtl31AYvvd/e73O3V4ZkNzuGpTDndLSyc0dQRB2xrQqDePd20pEGIfqSp/GH3pRw==", + "devOptional": true, + "dependencies": { + "@types/node": "*", + "pg-protocol": "*", + "pg-types": "^4.0.1" + } + }, + "node_modules/@types/pg/node_modules/pg-types": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-4.0.2.tgz", + "integrity": "sha512-cRL3JpS3lKMGsKaWndugWQoLOCoP+Cic8oseVcbr0qhPzYD5DWXK+RZ9LY9wxRf7RQia4SCwQlXk0q6FCPrVng==", + "devOptional": true, + "dependencies": { + "pg-int8": "1.0.1", + "pg-numeric": "1.0.2", + "postgres-array": "~3.0.1", + "postgres-bytea": "~3.0.0", + "postgres-date": "~2.1.0", + "postgres-interval": "^3.0.0", + "postgres-range": "^1.1.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@types/pg/node_modules/postgres-array": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-3.0.2.tgz", + "integrity": "sha512-6faShkdFugNQCLwucjPcY5ARoW1SlbnrZjmGl0IrrqewpvxvhSLHimCVzqeuULCbG0fQv7Dtk1yDbG3xv7Veog==", + "devOptional": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/@types/pg/node_modules/postgres-bytea": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-3.0.0.tgz", + "integrity": "sha512-CNd4jim9RFPkObHSjVHlVrxoVQXz7quwNFpz7RY1okNNme49+sVyiTvTRobiLV548Hx/hb1BG+iE7h9493WzFw==", + "devOptional": true, + "dependencies": { + "obuf": "~1.1.2" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@types/pg/node_modules/postgres-date": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-2.1.0.tgz", + "integrity": "sha512-K7Juri8gtgXVcDfZttFKVmhglp7epKb1K4pgrkLxehjqkrgPhfG6OO8LHLkfaqkbpjNRnra018XwAr1yQFWGcA==", + "devOptional": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/@types/pg/node_modules/postgres-interval": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-3.0.0.tgz", + "integrity": "sha512-BSNDnbyZCXSxgA+1f5UU2GmwhoI0aU5yMxRGO8CdFEcY2BQF9xm/7MqKnYoM1nJDk8nONNWDk9WeSmePFhQdlw==", + "devOptional": true, + "engines": { + "node": ">=12" + } + }, "node_modules/@types/pug": { "version": "2.0.10", "resolved": "https://registry.npmjs.org/@types/pug/-/pug-2.0.10.tgz", @@ -3548,6 +4171,12 @@ "node": ">=8" } }, + "node_modules/fs-monkey": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.5.tgz", + "integrity": "sha512-8uMbBjrhzW76TYgEV27Y5E//W2f/lTFmx78P2w19FZSxarhI/798APGQyuGCwmkNxgwGRhrLfvWyLBvNtuOmew==", + "optional": true + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -4028,6 +4657,495 @@ "es5-ext": "~0.10.2" } }, + "node_modules/lucia": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/lucia/-/lucia-3.1.1.tgz", + "integrity": "sha512-Ygvgnqq7Ha7lYVaZATPwkPD2s2Qlsm71Z2o0byx/abNBfFldCRow5sNii6RqMsuMpK957RAI3Gw4/aWoagkc7A==", + "dependencies": { + "oslo": "1.0.1" + } + }, + "node_modules/lucia/node_modules/@node-rs/argon2": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@node-rs/argon2/-/argon2-1.7.2.tgz", + "integrity": "sha512-+H6pc3M1vIX9YnG59YW7prHhhpv19P8YyxlXHnnFzTimf2q+kKDF7mGWbhvN9STqIY+P70Patn0Q6qb6Ib5/4g==", + "engines": { + "node": ">= 10" + }, + "optionalDependencies": { + "@node-rs/argon2-android-arm-eabi": "1.7.2", + "@node-rs/argon2-android-arm64": "1.7.2", + "@node-rs/argon2-darwin-arm64": "1.7.2", + "@node-rs/argon2-darwin-x64": "1.7.2", + "@node-rs/argon2-freebsd-x64": "1.7.2", + "@node-rs/argon2-linux-arm-gnueabihf": "1.7.2", + "@node-rs/argon2-linux-arm64-gnu": "1.7.2", + "@node-rs/argon2-linux-arm64-musl": "1.7.2", + "@node-rs/argon2-linux-x64-gnu": "1.7.2", + "@node-rs/argon2-linux-x64-musl": "1.7.2", + "@node-rs/argon2-wasm32-wasi": "1.7.2", + "@node-rs/argon2-win32-arm64-msvc": "1.7.2", + "@node-rs/argon2-win32-ia32-msvc": "1.7.2", + "@node-rs/argon2-win32-x64-msvc": "1.7.2" + } + }, + "node_modules/lucia/node_modules/@node-rs/argon2-android-arm-eabi": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-android-arm-eabi/-/argon2-android-arm-eabi-1.7.2.tgz", + "integrity": "sha512-WhW84XOzdR4AOGc4BJvIg5lCRVBL0pXp/PPCe8QCyWw493p7VdNCdYpr2xdtjS/0zImmY85HNB/6zpzjLRTT/A==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/lucia/node_modules/@node-rs/argon2-android-arm64": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-android-arm64/-/argon2-android-arm64-1.7.2.tgz", + "integrity": "sha512-CdtayHSMIyDuVhSYFirwA757c4foQuyTjpysgFJLHweP9C7uDiBf9WBYij+UyabpaCadJ0wPyK6Vakinvlk4/g==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/lucia/node_modules/@node-rs/argon2-darwin-arm64": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-darwin-arm64/-/argon2-darwin-arm64-1.7.2.tgz", + "integrity": "sha512-hUOhtgYHTEyzX5sgMZVdXunONOus2HWpWydF5D/RYJ1mZ76FXRnFpQE40DqbzisdPIraKdn40m7JqkPP7wqdyg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/lucia/node_modules/@node-rs/argon2-darwin-x64": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-darwin-x64/-/argon2-darwin-x64-1.7.2.tgz", + "integrity": "sha512-lfs5HX+t542yUfcv6Aa/NeGD1nUCwyQNgnPEGcik71Ow6V13hkR1bHgmT1u3CHN4fBts0gW+DQEDsq1xlVgkvw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/lucia/node_modules/@node-rs/argon2-freebsd-x64": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-freebsd-x64/-/argon2-freebsd-x64-1.7.2.tgz", + "integrity": "sha512-ROoF+4VaCBJUjddrTN1hjuqSl89ppRcjVXJscSPJjWzTlbzFmGGovJvIzUBmCr/Oq3yM1zKHj6MP9oRD5cB+/g==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/lucia/node_modules/@node-rs/argon2-linux-arm-gnueabihf": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-arm-gnueabihf/-/argon2-linux-arm-gnueabihf-1.7.2.tgz", + "integrity": "sha512-CBSB8KPI8LS74Bcz3dYaa2/khULutz4vSDvFWUERlSLX+mPdDhoZi6UPuUPPF9e01w8AbiK1YCqlLUTm3tIMfw==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/lucia/node_modules/@node-rs/argon2-linux-arm64-gnu": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-arm64-gnu/-/argon2-linux-arm64-gnu-1.7.2.tgz", + "integrity": "sha512-6LBTug6ZiWFakP3X3Nqs7ZTM03gmcSWX4YvEn20HhhQE5NDrsrw3zNqGj0cJiNzKKIMSDDuj7uGy+ITEfNo4CA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/lucia/node_modules/@node-rs/argon2-linux-arm64-musl": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-arm64-musl/-/argon2-linux-arm64-musl-1.7.2.tgz", + "integrity": "sha512-KjhQ+ZPne29t9VRVeIif7JdKwQba+tM6CBNYBoJB1iON0CUKeqSQtZcHuTj9gkf2SNRG5bsU4ABcfxd0OKsKHg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/lucia/node_modules/@node-rs/argon2-linux-x64-gnu": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-x64-gnu/-/argon2-linux-x64-gnu-1.7.2.tgz", + "integrity": "sha512-BQvp+iLtKqomHz4q5t1aKoni9osgvUDU5sZtHAlFm5dRTlGHnympcQVATRE5GHyH9C6MIM9W7P1kqEeCLGPolQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/lucia/node_modules/@node-rs/argon2-linux-x64-musl": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-x64-musl/-/argon2-linux-x64-musl-1.7.2.tgz", + "integrity": "sha512-yXJudpBZQ98g+lWaHn9EzZ5KsAyqRdlpub/K+5NP7gHehb8wzBRIFAejIHAG0fvzQEEc86VOnV2koWIVZxWAvw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/lucia/node_modules/@node-rs/argon2-wasm32-wasi": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-wasm32-wasi/-/argon2-wasm32-wasi-1.7.2.tgz", + "integrity": "sha512-diXlVjJZY2GIV8ZDwUqXPhacXsFR0klGSv5D9f+XidwWXK4udtzDhkM/7N/Mb7h1HAWaxZ6IN9spYFjvWH1wqg==", + "cpu": [ + "wasm32" + ], + "optional": true, + "dependencies": { + "@napi-rs/wasm-runtime": "^0.1.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/lucia/node_modules/@node-rs/argon2-win32-arm64-msvc": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-win32-arm64-msvc/-/argon2-win32-arm64-msvc-1.7.2.tgz", + "integrity": "sha512-dhIBrY04P9nbmwzBpgERQDmmSu4YBZyeEE32t4TikMz5rQ07iaVC+JpGmtCBZoDIsLDHGC8cikENd3YEqpqIcA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/lucia/node_modules/@node-rs/argon2-win32-ia32-msvc": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-win32-ia32-msvc/-/argon2-win32-ia32-msvc-1.7.2.tgz", + "integrity": "sha512-o1tfqr8gyALCzuxBoQfvhxkeYMaw/0H8Gmt7klTYyEIBvEFu7SD5qytXO9Px7t5420nZL/Wy5cflg3IB1s57Pg==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/lucia/node_modules/@node-rs/argon2-win32-x64-msvc": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-win32-x64-msvc/-/argon2-win32-x64-msvc-1.7.2.tgz", + "integrity": "sha512-v0h53XUc7hNgWiWi0qcMcHvj9/kwuItI9NwLK4C+gtzT3UB0cedhfIL8HFMKThMXasy41ZdbpCF2Bi0kJoLNEg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/lucia/node_modules/@node-rs/bcrypt": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt/-/bcrypt-1.9.2.tgz", + "integrity": "sha512-FKUo9iCSIti+ldwoOlY1ztyIFhZxEgT7jZ/UCt/9bg1rLmNdbQQD2JKIMImDCqmTWuLPY4ZF4Q5MyOMIfDCd8Q==", + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "optionalDependencies": { + "@node-rs/bcrypt-android-arm-eabi": "1.9.2", + "@node-rs/bcrypt-android-arm64": "1.9.2", + "@node-rs/bcrypt-darwin-arm64": "1.9.2", + "@node-rs/bcrypt-darwin-x64": "1.9.2", + "@node-rs/bcrypt-freebsd-x64": "1.9.2", + "@node-rs/bcrypt-linux-arm-gnueabihf": "1.9.2", + "@node-rs/bcrypt-linux-arm64-gnu": "1.9.2", + "@node-rs/bcrypt-linux-arm64-musl": "1.9.2", + "@node-rs/bcrypt-linux-x64-gnu": "1.9.2", + "@node-rs/bcrypt-linux-x64-musl": "1.9.2", + "@node-rs/bcrypt-wasm32-wasi": "1.9.2", + "@node-rs/bcrypt-win32-arm64-msvc": "1.9.2", + "@node-rs/bcrypt-win32-ia32-msvc": "1.9.2", + "@node-rs/bcrypt-win32-x64-msvc": "1.9.2" + } + }, + "node_modules/lucia/node_modules/@node-rs/bcrypt-android-arm-eabi": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-android-arm-eabi/-/bcrypt-android-arm-eabi-1.9.2.tgz", + "integrity": "sha512-er/Q2khwpan9pczvTTqY/DJE4UU65u31xd0NkZlHUTKyB7djRhWfzoGexGx2GN+k831/RR3U8kKE/8QUHeO3hQ==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/lucia/node_modules/@node-rs/bcrypt-android-arm64": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-android-arm64/-/bcrypt-android-arm64-1.9.2.tgz", + "integrity": "sha512-OUYatOEG5vbLbF73q2TC8UqrDO81zUQxnaFD/OAB1hcm6J+ur0zJ8E53c35/DIqkTp7JarPMraC4rouJ2ugN4w==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/lucia/node_modules/@node-rs/bcrypt-darwin-arm64": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-darwin-arm64/-/bcrypt-darwin-arm64-1.9.2.tgz", + "integrity": "sha512-svJKsGbzMAxOB5oluOYneN4YkKUy26WSMgm3KOIhgoX30IeMilj+2jFN/5qrI0oDZ0Iczb3XyL5DuZFtEkdP8A==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/lucia/node_modules/@node-rs/bcrypt-darwin-x64": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-darwin-x64/-/bcrypt-darwin-x64-1.9.2.tgz", + "integrity": "sha512-9OrySjBi/rWix8NZWD/TrNbNcwMY0pAiMHdL09aJnJ07uPih83GGh1pq4UHCYFCMy7iTX8swOmDlGBUImkOZbg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/lucia/node_modules/@node-rs/bcrypt-freebsd-x64": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-freebsd-x64/-/bcrypt-freebsd-x64-1.9.2.tgz", + "integrity": "sha512-/djXV71RO6g5L1mI2pVvmp3x3pH7G4uKI3ODG1JBIXoz334oOcCMh40sB0uq0ljP8WEadker01p4T1rJE98fpg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/lucia/node_modules/@node-rs/bcrypt-linux-arm-gnueabihf": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-linux-arm-gnueabihf/-/bcrypt-linux-arm-gnueabihf-1.9.2.tgz", + "integrity": "sha512-F7wP950OTAooxEleUN4I2hqryGZK7hi1cSgRF13Wvbc597RFux35KiSxIXUA3mNt2DE7lV2PeceEtCOScaThWQ==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/lucia/node_modules/@node-rs/bcrypt-linux-arm64-gnu": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-linux-arm64-gnu/-/bcrypt-linux-arm64-gnu-1.9.2.tgz", + "integrity": "sha512-MehG+yQ0TgKMgKR1rO4hdvHkVsTM91Cof8qI9EJlS5+7+QSwfFA5O0zGwCkISD7bsyauJ5uJgcByGjpEobAHOg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/lucia/node_modules/@node-rs/bcrypt-linux-arm64-musl": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-linux-arm64-musl/-/bcrypt-linux-arm64-musl-1.9.2.tgz", + "integrity": "sha512-PRZTAJjOwKEGsIhmBvfNh81So+wGl4QyCFAt23j+KwBujLStjC0N3YaqtTlWVKG9tcriPtmMYiAQtXWIyIgg/w==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/lucia/node_modules/@node-rs/bcrypt-linux-x64-gnu": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-linux-x64-gnu/-/bcrypt-linux-x64-gnu-1.9.2.tgz", + "integrity": "sha512-5WfGO+O1m7nJ55WZ8XDq+ItA98Z4O7sNWsR+1nIj9YGT+Tx5zkQ2RBhpK6oCWZMluuZ0eKQ0FDmyP6K+2NDRIA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/lucia/node_modules/@node-rs/bcrypt-linux-x64-musl": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-linux-x64-musl/-/bcrypt-linux-x64-musl-1.9.2.tgz", + "integrity": "sha512-VjCn0388p6PMCVUYHgYmHZrKNc7WwNJRr2WLJsHbQRGDOKbpNL6YolCjQxUchcSPDhzwrq1cIdy4j0fpoXEsdw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/lucia/node_modules/@node-rs/bcrypt-wasm32-wasi": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-wasm32-wasi/-/bcrypt-wasm32-wasi-1.9.2.tgz", + "integrity": "sha512-P06aHfMzm9makwU+nM7WA65yQnS1xuqJ8l/6I/LvXjnl+lfB3DtJ2B0CSLtjnUGpUgcHbWl5gEbNnTPxSAirjQ==", + "cpu": [ + "wasm32" + ], + "optional": true, + "dependencies": { + "@napi-rs/wasm-runtime": "^0.1.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/lucia/node_modules/@node-rs/bcrypt-win32-arm64-msvc": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-win32-arm64-msvc/-/bcrypt-win32-arm64-msvc-1.9.2.tgz", + "integrity": "sha512-Iyo/Q5/eNw27VRd3mLBgh1b9b5fnT3QHTVwxv3Siv/MRAIfJXH/cTOe18qSwYQzNh0ZioW4yemFPYCWSZi7szA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/lucia/node_modules/@node-rs/bcrypt-win32-ia32-msvc": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-win32-ia32-msvc/-/bcrypt-win32-ia32-msvc-1.9.2.tgz", + "integrity": "sha512-6LHWMaPylyyHoS5863YpxAACVB8DWCxro5W6pQ4h8WKSgHpJp8Um9jphTdN0A2w45HZjUnfcFuiFFC+TbftjCw==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/lucia/node_modules/@node-rs/bcrypt-win32-x64-msvc": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-win32-x64-msvc/-/bcrypt-win32-x64-msvc-1.9.2.tgz", + "integrity": "sha512-vZ9T1MOaYkLO9FTyl28YX0SYJneiYTKNFgM8PUv8nas8xrD+7OzokA0fEtlNp6413T7IKSD/iG9qi8nTWsiyGg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/lucia/node_modules/oslo": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/oslo/-/oslo-1.0.1.tgz", + "integrity": "sha512-esfzZry+HfGgK/GCYkg7BRlLd3RH5aHa08wgLJPYjENXybi0BvXxGk0LbUj+lXfz2TkjPDHe4rB/o6JxRLHxBg==", + "dependencies": { + "@node-rs/argon2": "1.7.2", + "@node-rs/bcrypt": "1.9.2" + } + }, "node_modules/magic-string": { "version": "0.30.8", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.8.tgz", @@ -4070,6 +5188,27 @@ "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", "dev": true }, + "node_modules/memfs": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.5.3.tgz", + "integrity": "sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==", + "optional": true, + "dependencies": { + "fs-monkey": "^1.0.4" + }, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/memfs-browser": { + "version": "3.5.10302", + "resolved": "https://registry.npmjs.org/memfs-browser/-/memfs-browser-3.5.10302.tgz", + "integrity": "sha512-JJTc/nh3ig05O0gBBGZjTCPOyydaTxNF0uHYBrcc1gHNnO+KIHIvo0Y1FKCJsaei6FCl8C6xfQomXqu+cuzkIw==", + "optional": true, + "dependencies": { + "memfs": "3.5.3" + } + }, "node_modules/memoizee": { "version": "0.4.15", "resolved": "https://registry.npmjs.org/memoizee/-/memoizee-0.4.15.tgz", @@ -4343,6 +5482,12 @@ "node": ">= 6" } }, + "node_modules/obuf": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", + "devOptional": true + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -4352,6 +5497,15 @@ "wrappy": "1" } }, + "node_modules/oslo": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/oslo/-/oslo-1.2.0.tgz", + "integrity": "sha512-OoFX6rDsNcOQVAD2gQD/z03u4vEjWZLzJtwkmgfRF+KpQUXwdgEXErD7zNhyowmHwHefP+PM9Pw13pgpHMRlzw==", + "dependencies": { + "@node-rs/argon2": "1.7.0", + "@node-rs/bcrypt": "1.9.0" + } + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -4416,12 +5570,12 @@ } }, "node_modules/pg": { - "version": "8.11.4", - "resolved": "https://registry.npmjs.org/pg/-/pg-8.11.4.tgz", - "integrity": "sha512-pWb7JKPxGk1UFbtq7jQ0m3IfPpb7LLACCEyN8/u9DYEom+Q/BSKy+4TRl4+Hh003AOYhppB/z+QK87/hx/bk0w==", + "version": "8.11.5", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.11.5.tgz", + "integrity": "sha512-jqgNHSKL5cbDjFlHyYsCXmQDrfIX/3RsNwYqpd4N0Kt8niLuNoRNH+aazv6cOd43gPh9Y4DjQCtb+X0MH0Hvnw==", "devOptional": true, "dependencies": { - "pg-connection-string": "^2.6.3", + "pg-connection-string": "^2.6.4", "pg-pool": "^3.6.2", "pg-protocol": "^1.6.1", "pg-types": "^2.1.0", @@ -4450,9 +5604,9 @@ "optional": true }, "node_modules/pg-connection-string": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.3.tgz", - "integrity": "sha512-77FxhhKJQH+xJx6tDqkhhMa0nZvv3U1HYLDQgwZxZafVD583++O5LXn5oo5HaQZ0vXwYcZA1koYAJM3JvD6Gtw==", + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.4.tgz", + "integrity": "sha512-v+Z7W/0EO707aNMaAEfiGnGL9sxxumwLl2fJvCQtMn9Fxsg+lPpPkdcyBSv/KFgpGdYkMfn+EI1Or2EHjpgLCA==", "devOptional": true }, "node_modules/pg-int8": { @@ -4464,6 +5618,15 @@ "node": ">=4.0.0" } }, + "node_modules/pg-numeric": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pg-numeric/-/pg-numeric-1.0.2.tgz", + "integrity": "sha512-BM/Thnrw5jm2kKLE5uJkXqqExRUY/toLHda65XgFTBTFYZyopbKjBe29Ii3RbkvlsMoFwD+tHeGaCjjv0gHlyw==", + "devOptional": true, + "engines": { + "node": ">=4" + } + }, "node_modules/pg-pool": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.6.2.tgz", @@ -4740,6 +5903,12 @@ "node": ">=0.10.0" } }, + "node_modules/postgres-range": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/postgres-range/-/postgres-range-1.1.4.tgz", + "integrity": "sha512-i/hbxIE9803Alj/6ytL7UHQxRvZkI9O4Sy+J3HGc4F4oo/2eQAjTSNJ0bfxyse3bH0nuVesCk+3IRLaMtG3H6w==", + "devOptional": true + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -5593,7 +6762,7 @@ "version": "2.6.2", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "dev": true + "devOptional": true }, "node_modules/type": { "version": "2.7.2", @@ -5614,6 +6783,12 @@ "node": ">=14.17" } }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "devOptional": true + }, "node_modules/update-browserslist-db": { "version": "1.0.13", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", diff --git a/package.json b/package.json index 9613ca6..6b54d1a 100644 --- a/package.json +++ b/package.json @@ -21,11 +21,13 @@ "@sveltejs/kit": "^2.0.0", "@sveltejs/vite-plugin-svelte": "^3.0.0", "@tailwindcss/typography": "^0.5.12", + "@types/pg": "^8.11.4", "autoprefixer": "^10.4.19", "daisyui": "^4.9.0", "dotenv": "^16.4.5", "drizzle-kit": "^0.20.14", - "pg": "^8.11.4", + "lucia": "^3.1.1", + "pg": "^8.11.5", "postcss": "^8.4.38", "svelte": "^4.2.7", "svelte-check": "^3.6.0", @@ -36,7 +38,9 @@ }, "type": "module", "dependencies": { + "@lucia-auth/adapter-drizzle": "^1.0.7", "drizzle-orm": "^0.30.6", + "oslo": "^1.2.0", "postgres": "^3.4.4" } } diff --git a/src/app.d.ts b/src/app.d.ts index ede601a..65be1f9 100644 --- a/src/app.d.ts +++ b/src/app.d.ts @@ -1,12 +1,9 @@ -// See https://kit.svelte.dev/docs/types#app -// for information about these interfaces declare global { namespace App { - // interface Error {} - // interface Locals {} - // interface PageData {} - // interface PageState {} - // interface Platform {} + interface Locals { + user: import("lucia").User | null; + session: import("lucia").Session | null; + } } } diff --git a/src/hooks.server.ts b/src/hooks.server.ts new file mode 100644 index 0000000..4b48cbc --- /dev/null +++ b/src/hooks.server.ts @@ -0,0 +1,32 @@ +import { lucia } from "$lib/server/auth"; +import type { Handle } from "@sveltejs/kit"; + +export const handle: Handle = async ({ event, resolve }) => { + const sessionId = event.cookies.get(lucia.sessionCookieName); + if (!sessionId) { + event.locals.user = null; + event.locals.session = null; + return resolve(event); + } + + const { session, user } = await lucia.validateSession(sessionId); + if (session && session.fresh) { + const sessionCookie = lucia.createSessionCookie(session.id); + // sveltekit types deviates from the de-facto standard + // you can use 'as any' too + event.cookies.set(sessionCookie.name, sessionCookie.value, { + path: ".", + ...sessionCookie.attributes, + }); + } + if (!session) { + const sessionCookie = lucia.createBlankSessionCookie(); + event.cookies.set(sessionCookie.name, sessionCookie.value, { + path: ".", + ...sessionCookie.attributes, + }); + } + event.locals.user = user; + event.locals.session = session; + return resolve(event); +}; diff --git a/src/lib/db/db.server.ts b/src/lib/db/db.server.ts index 9288f10..f4fbc86 100644 --- a/src/lib/db/db.server.ts +++ b/src/lib/db/db.server.ts @@ -1,8 +1,9 @@ import { drizzle } from "drizzle-orm/postgres-js"; import postgres from "postgres"; import dotenv from "dotenv"; +import * as schema from "$lib/db/schema"; dotenv.config(); const { DATABASE_URL } = process.env; -const client = postgres(DATABASE_URL); -export const db = drizzle(client, {}); +const client = postgres(DATABASE_URL || ""); // Pass DATABASE_URL as a string argument +export const db = drizzle(client, { schema }); diff --git a/src/lib/db/schema.ts b/src/lib/db/schema.ts index f289086..e5f1264 100644 --- a/src/lib/db/schema.ts +++ b/src/lib/db/schema.ts @@ -1,4 +1,11 @@ -import { pgTable, json, text, serial } from "drizzle-orm/pg-core"; +import { + pgTable, + text, + timestamp, + json, + serial, + varchar, +} from "drizzle-orm/pg-core"; export const featuredAdventures = pgTable("featuredAdventures", { id: serial("id").primaryKey(), @@ -10,3 +17,22 @@ export const sharedAdventures = pgTable("sharedAdventures", { id: text("id").primaryKey(), data: json("data").notNull(), }); + +export const userTable = pgTable("user", { + id: text("id").primaryKey(), + username: text("username").notNull(), + hashed_password: varchar("hashed_password").notNull(), +}); + +// export type SelectUser = typeof userTable.$inferSelect; + +export const sessionTable = pgTable("session", { + id: text("id").primaryKey(), + userId: text("user_id") + .notNull() + .references(() => userTable.id), + expiresAt: timestamp("expires_at", { + withTimezone: true, + mode: "date", + }).notNull(), +}); diff --git a/src/lib/server/auth.ts b/src/lib/server/auth.ts new file mode 100644 index 0000000..2907892 --- /dev/null +++ b/src/lib/server/auth.ts @@ -0,0 +1,38 @@ +import { DrizzlePostgreSQLAdapter } from "@lucia-auth/adapter-drizzle"; +import { Lucia } from "lucia"; +import { dev } from "$app/environment"; +import { userTable, sessionTable } from "$lib/db/schema"; +import { db } from "$lib/db/db.server"; +import { Argon2id } from "oslo/password"; + +const adapter = new DrizzlePostgreSQLAdapter(db, sessionTable, userTable); + +export const lucia = new Lucia(adapter, { + sessionCookie: { + attributes: { + secure: !dev, + }, + }, + getUserAttributes: (attributes) => { + return { + // attributes has the type of DatabaseUserAttributes + username: attributes.username, + }; + }, +}); + +declare module "lucia" { + interface Register { + Lucia: typeof lucia; + DatabaseUserAttributes: DatabaseUserAttributes; + } +} + +interface DatabaseUserAttributes { + username: string; +} +export interface DatabaseUser { + id: string; + username: string; + hashed_password: string; +} diff --git a/src/routes/login/+page.server.ts b/src/routes/login/+page.server.ts new file mode 100644 index 0000000..5f05494 --- /dev/null +++ b/src/routes/login/+page.server.ts @@ -0,0 +1,76 @@ +import { lucia } from "$lib/server/auth"; +import { fail, redirect } from "@sveltejs/kit"; +import { Argon2id } from "oslo/password"; +import { db } from "$lib/db/db.server"; + +import type { Actions, PageServerLoad } from "./$types"; +import type { DatabaseUser } from "$lib/server/auth"; +import { userTable } from "$lib/db/schema"; +import { eq } from "drizzle-orm"; + +export const load: PageServerLoad = async (event) => { + if (event.locals.user) { + return redirect(302, "/"); + } + return {}; +}; + +export const actions: Actions = { + default: async (event) => { + const formData = await event.request.formData(); + const username = formData.get("username"); + const password = formData.get("password"); + + if ( + typeof username !== "string" || + username.length < 3 || + username.length > 31 || + !/^[a-z0-9_-]+$/.test(username) + ) { + return fail(400, { + message: "Invalid username", + }); + } + if ( + typeof password !== "string" || + password.length < 6 || + password.length > 255 + ) { + return fail(400, { + message: "Invalid password", + }); + } + + const existingUser = await db + .select() + .from(userTable) + .where(eq(userTable.username, username)) + .limit(1) + .then((results) => results[0] as unknown as DatabaseUser | undefined); + + if (!existingUser) { + return fail(400, { + message: "Incorrect username or password", + }); + } + + const validPassword = await new Argon2id().verify( + existingUser.hashed_password, + password + ); + if (!validPassword) { + return fail(400, { + message: "Incorrect username or password", + }); + } + + const session = await lucia.createSession(existingUser.id, {}); + const sessionCookie = lucia.createSessionCookie(session.id); + event.cookies.set(sessionCookie.name, sessionCookie.value, { + path: ".", + ...sessionCookie.attributes, + }); + + return redirect(302, "/"); + }, +}; diff --git a/src/routes/login/+page.svelte b/src/routes/login/+page.svelte new file mode 100644 index 0000000..2d865c4 --- /dev/null +++ b/src/routes/login/+page.svelte @@ -0,0 +1,13 @@ + + + +

Sign in

+
+ +
+ +
+ +
diff --git a/src/routes/signup/+page.server.ts b/src/routes/signup/+page.server.ts new file mode 100644 index 0000000..b6a99f9 --- /dev/null +++ b/src/routes/signup/+page.server.ts @@ -0,0 +1,60 @@ +// routes/signup/+page.server.ts +import { lucia } from "$lib/server/auth"; +import { fail, redirect } from "@sveltejs/kit"; +import { generateId } from "lucia"; +import { Argon2id } from "oslo/password"; +import { db } from "$lib/db/db.server"; + +import type { Actions } from "./$types"; +import { userTable } from "$lib/db/schema"; + +export const actions: Actions = { + default: async (event) => { + const formData = await event.request.formData(); + const username = formData.get("username"); + const password = formData.get("password"); + // 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 + if ( + typeof username !== "string" || + username.length < 3 || + username.length > 31 || + !/^[a-z0-9_-]+$/.test(username) + ) { + return fail(400, { + message: "Invalid username", + }); + } + if ( + typeof password !== "string" || + password.length < 6 || + password.length > 255 + ) { + return fail(400, { + message: "Invalid password", + }); + } + + const userId = generateId(15); + const hashedPassword = await new Argon2id().hash(password); + + // TODO: check if username is already used + await db + .insert(userTable) + .values({ + id: userId, + username: username, + hashed_password: hashedPassword, + }) + .execute(); + + const session = await lucia.createSession(userId, {}); + const sessionCookie = lucia.createSessionCookie(session.id); + event.cookies.set(sessionCookie.name, sessionCookie.value, { + path: ".", + ...sessionCookie.attributes, + }); + + redirect(302, "/"); + }, +}; diff --git a/src/routes/signup/+page.svelte b/src/routes/signup/+page.svelte new file mode 100644 index 0000000..a553a36 --- /dev/null +++ b/src/routes/signup/+page.svelte @@ -0,0 +1,13 @@ + + + +

Sign up

+
+ +
+ +
+ +
From ba6a5283fea686d001d63a31505fd4d18478ada1 Mon Sep 17 00:00:00 2001 From: Sean Morley Date: Wed, 3 Apr 2024 22:59:05 +0000 Subject: [PATCH 02/28] Add user first and last name fields to signup form --- migrations/0007_nervous_scalphunter.sql | 2 + migrations/meta/0007_snapshot.json | 147 ++++++++++++++++++++++++ migrations/meta/_journal.json | 7 ++ src/lib/components/Navbar.svelte | 2 +- src/lib/db/schema.ts | 2 + src/lib/server/auth.ts | 13 ++- src/routes/+layout.server.ts | 12 ++ src/routes/+layout.svelte | 2 +- src/routes/+page.server.ts | 30 +++++ src/routes/+page.svelte | 13 +++ src/routes/signup/+page.server.ts | 49 +++++++- src/routes/signup/+page.svelte | 4 + 12 files changed, 273 insertions(+), 10 deletions(-) create mode 100644 migrations/0007_nervous_scalphunter.sql create mode 100644 migrations/meta/0007_snapshot.json create mode 100644 src/routes/+layout.server.ts create mode 100644 src/routes/+page.server.ts diff --git a/migrations/0007_nervous_scalphunter.sql b/migrations/0007_nervous_scalphunter.sql new file mode 100644 index 0000000..baf412d --- /dev/null +++ b/migrations/0007_nervous_scalphunter.sql @@ -0,0 +1,2 @@ +ALTER TABLE "user" ADD COLUMN "first_name" text NOT NULL;--> statement-breakpoint +ALTER TABLE "user" ADD COLUMN "last_name" text NOT NULL; \ No newline at end of file diff --git a/migrations/meta/0007_snapshot.json b/migrations/meta/0007_snapshot.json new file mode 100644 index 0000000..e436475 --- /dev/null +++ b/migrations/meta/0007_snapshot.json @@ -0,0 +1,147 @@ +{ + "id": "2039600b-1f5f-4f37-84dd-bb40636855e7", + "prevId": "b0849b3e-02e1-42e1-b07c-6fa613c98e82", + "version": "5", + "dialect": "pg", + "tables": { + "featuredAdventures": { + "name": "featuredAdventures", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "location": { + "name": "location", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "session": { + "name": "session", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "session_user_id_user_id_fk": { + "name": "session_user_id_user_id_fk", + "tableFrom": "session", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "sharedAdventures": { + "name": "sharedAdventures", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "data": { + "name": "data", + "type": "json", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "user": { + "name": "user", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "first_name": { + "name": "first_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "last_name": { + "name": "last_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "hashed_password": { + "name": "hashed_password", + "type": "varchar", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + } + }, + "enums": {}, + "schemas": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/migrations/meta/_journal.json b/migrations/meta/_journal.json index ff1ddfe..6550bd1 100644 --- a/migrations/meta/_journal.json +++ b/migrations/meta/_journal.json @@ -50,6 +50,13 @@ "when": 1712105206127, "tag": "0006_melted_leech", "breakpoints": true + }, + { + "idx": 7, + "version": "5", + "when": 1712167204757, + "tag": "0007_nervous_scalphunter", + "breakpoints": true } ] } \ No newline at end of file diff --git a/src/lib/components/Navbar.svelte b/src/lib/components/Navbar.svelte index 3a47ee9..f1b0785 100644 --- a/src/lib/components/Navbar.svelte +++ b/src/lib/components/Navbar.svelte @@ -1,7 +1,7 @@ -

Sign in

-
- -
- -
- -
+
+

Sign in

+
+ +
+
+ +
+ +
+ +
+
+ +
+
+ {#if quote != ""} + "{quote}" + {/if} + +
+
From d4e18db4768ed516efd6b0dca90a9b42a1c9d591 Mon Sep 17 00:00:00 2001 From: Sean Morley <98704938+seanmorley15@users.noreply.github.com> Date: Thu, 4 Apr 2024 10:04:52 -0400 Subject: [PATCH 04/28] Create CONTRIBUTING.md --- CONTRIBUTING.md | 92 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..9f1ab7b --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,92 @@ +# Contributing + +When contributing to this repository, please first discuss the change you wish to make via issue, +email, or any other method with the owners of this repository before making a change. + +Please note we have a code of conduct, please follow it in all your interactions with the project. + +## Pull Request Process + +1. Ensure any install or build dependencies are removed before the end of the layer when doing a + build. +2. Update the README.md with details of changes to the interface, this includes new environment + variables, exposed ports, useful file locations and container parameters. +3. Increase the version numbers in any examples files and the README.md to the new version that this + Pull Request would represent. The versioning scheme we use is [SemVer](http://semver.org/). +4. You may merge the Pull Request in once you have the sign-off of two other developers, or if you + do not have permission to do that, you may request the second reviewer to merge it for you. + +## Code of Conduct + +### Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, gender identity and expression, level of experience, +nationality, personal appearance, race, religion, or sexual identity and +orientation. + +### Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or +advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +### Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +### Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +### Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at [INSERT EMAIL ADDRESS]. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +### Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at [http://contributor-covenant.org/version/1/4][version] + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ From f3359170478062ce7f69945b81a804cf1fe6429a Mon Sep 17 00:00:00 2001 From: Sean Morley <98704938+seanmorley15@users.noreply.github.com> Date: Thu, 4 Apr 2024 10:53:14 -0400 Subject: [PATCH 05/28] Update CONTRIBUTING.md --- CONTRIBUTING.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9f1ab7b..4934d0e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,4 +1,4 @@ -# Contributing +# Contributing to AdventureLog When contributing to this repository, please first discuss the change you wish to make via issue, email, or any other method with the owners of this repository before making a change. @@ -7,8 +7,7 @@ Please note we have a code of conduct, please follow it in all your interactions ## Pull Request Process -1. Ensure any install or build dependencies are removed before the end of the layer when doing a - build. +1. Please make sure you create an issue first for your change so you can link any pull requests to this issue. There should be a clear relationship between pull requests and issues. 2. Update the README.md with details of changes to the interface, this includes new environment variables, exposed ports, useful file locations and container parameters. 3. Increase the version numbers in any examples files and the README.md to the new version that this From 3ab2918ed2d1ebc5d0cb3cbbc709c66ca438e32c Mon Sep 17 00:00:00 2001 From: Sean Morley Date: Fri, 5 Apr 2024 22:17:20 +0000 Subject: [PATCH 06/28] Add user name to Navbar component --- src/lib/components/Navbar.svelte | 4 ++++ src/routes/+layout.svelte | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/lib/components/Navbar.svelte b/src/lib/components/Navbar.svelte index f1b0785..b11e28e 100644 --- a/src/lib/components/Navbar.svelte +++ b/src/lib/components/Navbar.svelte @@ -2,6 +2,7 @@ import { visitCount } from "$lib/utils/stores/visitCountStore"; import { goto } from "$app/navigation"; import type { DatabaseUser } from "$lib/server/auth"; + export let user: any; async function goHome() { goto("/"); } @@ -47,5 +48,8 @@ diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index 1d600fe..cfa6854 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -1,12 +1,12 @@ - +
From cd11ce2d9750a951401ce1f33a38d04506bc0b59 Mon Sep 17 00:00:00 2001 From: Sean Morley Date: Fri, 5 Apr 2024 22:22:09 +0000 Subject: [PATCH 07/28] Add login functionality to Navbar component --- src/lib/components/Navbar.svelte | 11 ++++++++++- src/routes/+layout.svelte | 1 + 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/lib/components/Navbar.svelte b/src/lib/components/Navbar.svelte index b11e28e..cd18995 100644 --- a/src/lib/components/Navbar.svelte +++ b/src/lib/components/Navbar.svelte @@ -1,4 +1,5 @@ +
From de2dd6cd7ecb09337abc907094a5f8602683b863 Mon Sep 17 00:00:00 2001 From: Sean Morley Date: Sat, 6 Apr 2024 01:59:10 +0000 Subject: [PATCH 08/28] Add UserAvatar component and update Navbar.svelte --- src/lib/components/Navbar.svelte | 6 ++++++ src/lib/components/UserAvatar.svelte | 10 ++++++++++ src/routes/+page.svelte | 6 ------ 3 files changed, 16 insertions(+), 6 deletions(-) create mode 100644 src/lib/components/UserAvatar.svelte diff --git a/src/lib/components/Navbar.svelte b/src/lib/components/Navbar.svelte index cd18995..4343618 100644 --- a/src/lib/components/Navbar.svelte +++ b/src/lib/components/Navbar.svelte @@ -4,6 +4,7 @@ import { goto } from "$app/navigation"; import type { DatabaseUser } from "$lib/server/auth"; export let user: any; + import UserAvatar from "./UserAvatar.svelte"; async function goHome() { goto("/"); } @@ -16,6 +17,9 @@ async function toToLogin() { goto("/login"); } + async function toToSignup() { + goto("/signup"); + } let count = 0; visitCount.subscribe((value) => { @@ -54,8 +58,10 @@

Adventures: {count}

{#if !user} + {/if} {#if user} +
diff --git a/src/lib/components/UserAvatar.svelte b/src/lib/components/UserAvatar.svelte new file mode 100644 index 0000000..f1b983d --- /dev/null +++ b/src/lib/components/UserAvatar.svelte @@ -0,0 +1,10 @@ + + +
+
+ {firstLetter} +
+
diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index 04d4fa2..497dd31 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -31,9 +31,3 @@ - -{#if data.user} -
- -
-{/if} From 98f449018b3b8d0c8311f506c1a2d94e2c5d66f4 Mon Sep 17 00:00:00 2001 From: Sean Morley Date: Sat, 6 Apr 2024 02:01:34 +0000 Subject: [PATCH 09/28] Add generate step to Dockerfile --- Dockerfile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Dockerfile b/Dockerfile index 97eb7d7..c205a28 100644 --- a/Dockerfile +++ b/Dockerfile @@ -16,6 +16,8 @@ RUN npm ci # Build SvelteKit app RUN npm run build +RUN npm run generate + # Expose the port that the app is listening on EXPOSE 3000 From 1b9edeb61f2b09ed696a28e872d436d56f25642f Mon Sep 17 00:00:00 2001 From: Sean Morley Date: Sat, 6 Apr 2024 12:27:42 +0000 Subject: [PATCH 10/28] Update Navbar component and add logout functionality --- .env.example | 1 + src/lib/components/Navbar.svelte | 4 ++-- src/routes/+page.server.ts | 1 + 3 files changed, 4 insertions(+), 2 deletions(-) create mode 100644 .env.example diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..4cc714a --- /dev/null +++ b/.env.example @@ -0,0 +1 @@ +DATABASE_URL= \ No newline at end of file diff --git a/src/lib/components/Navbar.svelte b/src/lib/components/Navbar.svelte index 4343618..36ffccd 100644 --- a/src/lib/components/Navbar.svelte +++ b/src/lib/components/Navbar.svelte @@ -62,8 +62,8 @@ {/if} {#if user} -
- + +
{/if} diff --git a/src/routes/+page.server.ts b/src/routes/+page.server.ts index d335f9e..4838575 100644 --- a/src/routes/+page.server.ts +++ b/src/routes/+page.server.ts @@ -13,6 +13,7 @@ export const load: PageServerLoad = async (event) => { }; }; +// handle the logout action export const actions: Actions = { default: async (event) => { if (!event.locals.session) { From ea79fd2d76a76518727817df01ee93bf00ede2bc Mon Sep 17 00:00:00 2001 From: Sean Morley Date: Sat, 6 Apr 2024 12:54:17 +0000 Subject: [PATCH 11/28] Add log page, update database schema, and API endpoint for user visits --- migrations/0009_spotty_madame_web.sql | 5 + migrations/meta/0009_snapshot.json | 201 ++++++++++++++++++++++++++ migrations/meta/_journal.json | 7 + src/lib/components/Navbar.svelte | 10 +- src/lib/db/schema.ts | 3 +- src/routes/api/visits/+server.ts | 10 +- src/routes/log/+page.server.ts | 13 ++ src/routes/log/+page.svelte | 7 +- 8 files changed, 245 insertions(+), 11 deletions(-) create mode 100644 migrations/0009_spotty_madame_web.sql create mode 100644 migrations/meta/0009_snapshot.json create mode 100644 src/routes/log/+page.server.ts diff --git a/migrations/0009_spotty_madame_web.sql b/migrations/0009_spotty_madame_web.sql new file mode 100644 index 0000000..900b8d7 --- /dev/null +++ b/migrations/0009_spotty_madame_web.sql @@ -0,0 +1,5 @@ +ALTER TABLE "userVisitedAdventures" RENAME COLUMN "adventure_visited" TO "adventure_id";--> statement-breakpoint +ALTER TABLE "userVisitedAdventures" ADD PRIMARY KEY ("adventure_id");--> statement-breakpoint +ALTER TABLE "userVisitedAdventures" ALTER COLUMN "adventure_id" SET DATA TYPE serial;--> statement-breakpoint +ALTER TABLE "userVisitedAdventures" ALTER COLUMN "adventure_id" SET NOT NULL;--> statement-breakpoint +ALTER TABLE "userVisitedAdventures" ADD COLUMN "visited_date" text; \ No newline at end of file diff --git a/migrations/meta/0009_snapshot.json b/migrations/meta/0009_snapshot.json new file mode 100644 index 0000000..46e2615 --- /dev/null +++ b/migrations/meta/0009_snapshot.json @@ -0,0 +1,201 @@ +{ + "id": "1d83805d-6ee2-4dae-87a6-5af6f2d5add3", + "prevId": "ec5af5e4-8522-4383-af47-412fb43c7cc3", + "version": "5", + "dialect": "pg", + "tables": { + "featuredAdventures": { + "name": "featuredAdventures", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "location": { + "name": "location", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "session": { + "name": "session", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "session_user_id_user_id_fk": { + "name": "session_user_id_user_id_fk", + "tableFrom": "session", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "sharedAdventures": { + "name": "sharedAdventures", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "data": { + "name": "data", + "type": "json", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "user": { + "name": "user", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "first_name": { + "name": "first_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "last_name": { + "name": "last_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "hashed_password": { + "name": "hashed_password", + "type": "varchar", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "userVisitedAdventures": { + "name": "userVisitedAdventures", + "schema": "", + "columns": { + "adventure_id": { + "name": "adventure_id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "adventure_name": { + "name": "adventure_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "location": { + "name": "location", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "visited_date": { + "name": "visited_date", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "userVisitedAdventures_user_id_user_id_fk": { + "name": "userVisitedAdventures_user_id_user_id_fk", + "tableFrom": "userVisitedAdventures", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + } + }, + "enums": {}, + "schemas": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/migrations/meta/_journal.json b/migrations/meta/_journal.json index 730cbd1..226286d 100644 --- a/migrations/meta/_journal.json +++ b/migrations/meta/_journal.json @@ -64,6 +64,13 @@ "when": 1712186591227, "tag": "0008_romantic_maria_hill", "breakpoints": true + }, + { + "idx": 9, + "version": "5", + "when": 1712407140727, + "tag": "0009_spotty_madame_web", + "breakpoints": true } ] } \ No newline at end of file diff --git a/src/lib/components/Navbar.svelte b/src/lib/components/Navbar.svelte index 36ffccd..f05c718 100644 --- a/src/lib/components/Navbar.svelte +++ b/src/lib/components/Navbar.svelte @@ -43,10 +43,12 @@ class="btn btn-primary my-2 md:my-0 md:mr-4 md:ml-2" on:click={goHome}>Home - + {#if user} + + {/if} diff --git a/src/lib/db/schema.ts b/src/lib/db/schema.ts index 6e252a2..2800cad 100644 --- a/src/lib/db/schema.ts +++ b/src/lib/db/schema.ts @@ -40,10 +40,11 @@ export const sessionTable = pgTable("session", { }); export const userVisitedAdventures = pgTable("userVisitedAdventures", { + adventureID: serial("adventure_id").primaryKey(), userId: text("user_id") .notNull() .references(() => userTable.id), adventureName: text("adventure_name").notNull(), location: text("location"), - adventureVistied: text("adventure_visited"), + visitedDate: text("visited_date"), }); diff --git a/src/routes/api/visits/+server.ts b/src/routes/api/visits/+server.ts index d6d9aba..3f9c243 100644 --- a/src/routes/api/visits/+server.ts +++ b/src/routes/api/visits/+server.ts @@ -3,6 +3,7 @@ import type { RequestEvent } from "@sveltejs/kit"; import { userVisitedAdventures } from "$lib/db/schema"; import { db } from "$lib/db/db.server"; import { eq } from "drizzle-orm"; +import type { Adventure } from "$lib/utils/types"; // Gets all the adventures that the user has visited export async function GET(event: RequestEvent): Promise { @@ -14,16 +15,19 @@ export async function GET(event: RequestEvent): Promise { }, }); } - let result = await db .select() .from(userVisitedAdventures) .where(eq(userVisitedAdventures.userId, event.locals.user.id)) .execute(); - return new Response( JSON.stringify({ - result: result, + adventures: result.map((item) => ({ + id: item.adventureID, + name: item.adventureName, + location: item.location, + created: item.visitedDate, + })), }), { status: 200, diff --git a/src/routes/log/+page.server.ts b/src/routes/log/+page.server.ts new file mode 100644 index 0000000..41f111f --- /dev/null +++ b/src/routes/log/+page.server.ts @@ -0,0 +1,13 @@ +import { redirect } from "@sveltejs/kit"; +import type { PageServerLoad } from "./$types"; + +export const load: PageServerLoad = async (event) => { + if (!event.locals.user) { + return redirect(302, "/login"); + } + const response = await event.fetch("/api/visits"); + const result = await response.json(); + return { + result, + }; +}; diff --git a/src/routes/log/+page.svelte b/src/routes/log/+page.svelte index 5af4f81..451f01e 100644 --- a/src/routes/log/+page.svelte +++ b/src/routes/log/+page.svelte @@ -1,4 +1,6 @@
@@ -183,15 +207,15 @@
- {#each data.result.adventures as adventure (adventure.id)} + {#each adventures as adventure (adventure.id)} {/each}
diff --git a/src/services/adventureService.ts b/src/services/adventureService.ts index c994ee9..5ae6374 100644 --- a/src/services/adventureService.ts +++ b/src/services/adventureService.ts @@ -7,13 +7,13 @@ import { visitCount } from "$lib/utils/stores/visitCountStore"; // Check if localStorage is available (browser environment) const isBrowser = typeof window !== "undefined"; -// Load adventures from localStorage on startup (only in the browser) -if (isBrowser) { - const storedAdventures = localStorage.getItem("adventures"); - if (storedAdventures) { - adventures = JSON.parse(storedAdventures); - } -} +// // Load adventures from localStorage on startup (only in the browser) +// if (isBrowser) { +// const storedAdventures = localStorage.getItem("adventures"); +// if (storedAdventures) { +// adventures = JSON.parse(storedAdventures); +// } +// } export function getNextId() { let nextId = Math.max(0, ...adventures.map((adventure) => adventure.id)) + 1; From ed9a579fc27adc51fada0862c349a69c4e86611e Mon Sep 17 00:00:00 2001 From: Sean Morley Date: Wed, 10 Apr 2024 14:51:51 +0000 Subject: [PATCH 17/28] Add POST endpoint to create new adventure and update adventureService --- src/routes/api/visits/+server.ts | 57 +++++++++++++++++++++++++++++--- src/routes/log/+page.svelte | 45 +++++++++++++++++++------ src/services/adventureService.ts | 16 +-------- 3 files changed, 88 insertions(+), 30 deletions(-) diff --git a/src/routes/api/visits/+server.ts b/src/routes/api/visits/+server.ts index 62083f5..e123ec2 100644 --- a/src/routes/api/visits/+server.ts +++ b/src/routes/api/visits/+server.ts @@ -62,14 +62,63 @@ export async function DELETE(event: RequestEvent): Promise { ) .execute(); - console.log(res); - console.log(id); - console.log(event.locals.user.id); - return new Response(JSON.stringify({ id: id, res: res }), { status: 200, headers: { "Content-Type": "application/json", }, }); +} + +// add the adventure to the user's visited list +export async function POST(event: RequestEvent): Promise { + if (!event.locals.user) { + return new Response(JSON.stringify({ error: "No user found" }), { + status: 401, + headers: { + "Content-Type": "application/json", + }, + }); + } + + // get properties from the body + const { name, location, created } = await event.request.json(); + + // insert the adventure to the user's visited list + await db + .insert(userVisitedAdventures) + .values({ + userId: event.locals.user.id, + adventureName: name, + location: location, + visitedDate: created, + }) + .execute(); +let res = await db + .select() + .from(userVisitedAdventures) + .where( + and( + eq(userVisitedAdventures.userId, event.locals.user.id), + eq(userVisitedAdventures.adventureName, name), + eq(userVisitedAdventures.location, location), + eq(userVisitedAdventures.visitedDate, created) + ) + ) + .execute(); + +// return a response with the adventure object values + return new Response( + JSON.stringify({ + adventure: { name, location, created }, + message: { message: "Adventure added" }, + id: res[0].adventureID + }), + { + status: 200, + headers: { + "Content-Type": "application/json", + }, + } + ); } \ No newline at end of file diff --git a/src/routes/log/+page.svelte b/src/routes/log/+page.svelte index afcaecb..2583b12 100644 --- a/src/routes/log/+page.svelte +++ b/src/routes/log/+page.svelte @@ -53,17 +53,40 @@ const createNewAdventure = () => { let currentDate = new Date(); let dateString = currentDate.toISOString().slice(0, 10); // Get date in "yyyy-mm-dd" format - const newAdventure: Adventure = { - id: getNextId(), - name: newName, - location: newLocation, - created: dateString, - }; - addAdventure(newAdventure); - newName = ""; // Reset newName and newLocation after adding adventure - newLocation = ""; - adventures = getAdventures(); // add to local array - showToast("added"); + // post to /api/visits + fetch("/api/visits", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + name: newName, + location: newLocation, + created: dateString, + }), + }) + .then((response) => response.json()) + .then((data) => { + console.log("Success:", data); + let newId = data.id; + console.log("New ID: " + newId); + console.log("New Name: " + newName); + adventures = [ + ...adventures, + { + id: newId, + name: newName, + location: newLocation, + created: dateString, + }, + ]; + newName = ""; // Reset newName and newLocation after adding adventure + newLocation = ""; + showToast("added"); + }) + .catch((error) => { + console.error("Error:", error); + }); }; // function triggerRemoveAdventure(event: { detail: number }) { diff --git a/src/services/adventureService.ts b/src/services/adventureService.ts index 5ae6374..7088fc4 100644 --- a/src/services/adventureService.ts +++ b/src/services/adventureService.ts @@ -7,13 +7,7 @@ import { visitCount } from "$lib/utils/stores/visitCountStore"; // Check if localStorage is available (browser environment) const isBrowser = typeof window !== "undefined"; -// // Load adventures from localStorage on startup (only in the browser) -// if (isBrowser) { -// const storedAdventures = localStorage.getItem("adventures"); -// if (storedAdventures) { -// adventures = JSON.parse(storedAdventures); -// } -// } +// export function getNextId() { let nextId = Math.max(0, ...adventures.map((adventure) => adventure.id)) + 1; @@ -37,14 +31,6 @@ export function getAdventures(): Adventure[] { return adventures; } -export function removeAdventure(event: { detail: number }) { - adventures = adventures.filter((adventure) => adventure.id !== event.detail); - if (isBrowser) { - localStorage.setItem("adventures", JSON.stringify(adventures)); - visitCount.update((n) => n - 1); - } -} - export function saveEdit(adventure: Adventure) { let editId = adventure.id; console.log("saving edit"); From 45545fd8ce3a37787b94026b6b0f90941f99f3e7 Mon Sep 17 00:00:00 2001 From: Sean Morley Date: Wed, 10 Apr 2024 14:58:42 +0000 Subject: [PATCH 18/28] Add PUT route to update existing adventure and refactor adventureService --- src/routes/api/visits/+server.ts | 44 +++++++++++++++++++++++++++ src/routes/log/+page.svelte | 51 +++++++++++++++++++------------- src/services/adventureService.ts | 22 -------------- 3 files changed, 74 insertions(+), 43 deletions(-) diff --git a/src/routes/api/visits/+server.ts b/src/routes/api/visits/+server.ts index e123ec2..6d3bd84 100644 --- a/src/routes/api/visits/+server.ts +++ b/src/routes/api/visits/+server.ts @@ -121,4 +121,48 @@ let res = await db }, } ); +} + +// put route to update existing adventure +export async function PUT(event: RequestEvent): Promise { + if (!event.locals.user) { + return new Response(JSON.stringify({ error: "No user found" }), { + status: 401, + headers: { + "Content-Type": "application/json", + }, + }); + } + + // get properties from the body + const { id, name, location, created } = await event.request.json(); + + // update the adventure in the user's visited list + await db + .update(userVisitedAdventures) + .set({ + adventureName: name, + location: location, + visitedDate: created, + }) + .where( + and( + eq(userVisitedAdventures.userId, event.locals.user.id), + eq(userVisitedAdventures.adventureID, Number(id)) + ) + ) + .execute(); + + return new Response( + JSON.stringify({ + adventure: { id, name, location, created }, + message: { message: "Adventure updated" }, + }), + { + status: 200, + headers: { + "Content-Type": "application/json", + }, + } + ); } \ No newline at end of file diff --git a/src/routes/log/+page.svelte b/src/routes/log/+page.svelte index 2583b12..be07c59 100644 --- a/src/routes/log/+page.svelte +++ b/src/routes/log/+page.svelte @@ -9,7 +9,6 @@ clearAdventures, getAdventures, getNextId, - saveEdit, } from "../../services/adventureService"; import { onMount } from "svelte"; import { exportData } from "../../services/export"; @@ -67,10 +66,8 @@ }) .then((response) => response.json()) .then((data) => { - console.log("Success:", data); let newId = data.id; - console.log("New ID: " + newId); - console.log("New Name: " + newName); + // add to local array for instant view update adventures = [ ...adventures, { @@ -89,25 +86,37 @@ }); }; - // function triggerRemoveAdventure(event: { detail: number }) { - // removeAdventure(event); - // showToast("removed"); - // adventures = getAdventures(); - // // remove from data.result.adventures - // data.result.adventures = data.result.adventures.filter( - // (adventure: Adventure) => adventure.id !== event.detail, - // ); - // } - function saveAdventure(event: { detail: Adventure }) { console.log("Event" + event.detail); - saveEdit(event.detail); - editId = NaN; - editName = ""; - editLocation = ""; - editCreated = ""; - adventures = getAdventures(); - showToast("edited"); + // put request to /api/visits with id and advneture data + fetch("/api/visits", { + method: "PUT", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + id: event.detail.id, + name: event.detail.name, + location: event.detail.location, + created: event.detail.created, + }), + }) + .then((response) => response.json()) + .then((data) => { + console.log("Success:", data); + // update local array with new data + adventures = adventures.map((adventure) => + adventure.id === event.detail.id ? event.detail : adventure, + ); + editId = NaN; + editName = ""; + editLocation = ""; + editCreated = ""; + showToast("edited"); + }) + .catch((error) => { + console.error("Error:", error); + }); } function editAdventure(event: { detail: number }) { diff --git a/src/services/adventureService.ts b/src/services/adventureService.ts index 7088fc4..9e984f4 100644 --- a/src/services/adventureService.ts +++ b/src/services/adventureService.ts @@ -31,28 +31,6 @@ export function getAdventures(): Adventure[] { return adventures; } -export function saveEdit(adventure: Adventure) { - let editId = adventure.id; - console.log("saving edit"); - let editName = adventure.name; - let editLocation = adventure.location; - let editCreated = adventure.created; - let oldAdventure: Adventure | undefined = adventures.find( - (adventure) => adventure.id === editId - ); - console.log("old" + oldAdventure); - if (oldAdventure) { - oldAdventure.name = editName; - oldAdventure.location = editLocation; - oldAdventure.created = editCreated; - } - editId = NaN; - console.log("done"); - if (isBrowser) { - localStorage.setItem("adventures", JSON.stringify(adventures)); - } -} - export function clearAdventures() { adventures = []; if (isBrowser) { From ae5e77f670ee4493edd97b3c16ec00aae741a5c5 Mon Sep 17 00:00:00 2001 From: Sean Morley Date: Wed, 10 Apr 2024 15:19:19 +0000 Subject: [PATCH 19/28] Add config file and error page --- config.ts | 2 ++ src/routes/+error.svelte | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+) create mode 100644 config.ts create mode 100644 src/routes/+error.svelte diff --git a/config.ts b/config.ts new file mode 100644 index 0000000..f66ff54 --- /dev/null +++ b/config.ts @@ -0,0 +1,2 @@ +export let appVersion = "0.0.1"; +export let appTitle = "AdventureLog"; \ No newline at end of file diff --git a/src/routes/+error.svelte b/src/routes/+error.svelte new file mode 100644 index 0000000..c79020b --- /dev/null +++ b/src/routes/+error.svelte @@ -0,0 +1,18 @@ + + +
+

{$page.error.message}

+
+ +
+ +
From bc0e1b4db23c409ff95192bc35c28afe6b85f736 Mon Sep 17 00:00:00 2001 From: Sean Morley Date: Wed, 10 Apr 2024 17:47:20 +0000 Subject: [PATCH 20/28] Add visit count functionality to Navbar and Log page --- src/lib/components/Navbar.svelte | 16 +++++++---- src/lib/utils/stores/visitCountStore.ts | 4 ++- src/routes/+layout.server.ts | 2 +- src/routes/api/visitcount/+server.ts | 36 +++++++++++++++++++++++++ src/routes/log/+page.svelte | 8 ++++++ 5 files changed, 59 insertions(+), 7 deletions(-) create mode 100644 src/routes/api/visitcount/+server.ts diff --git a/src/lib/components/Navbar.svelte b/src/lib/components/Navbar.svelte index f05c718..d377bb2 100644 --- a/src/lib/components/Navbar.svelte +++ b/src/lib/components/Navbar.svelte @@ -5,6 +5,7 @@ import type { DatabaseUser } from "$lib/server/auth"; export let user: any; import UserAvatar from "./UserAvatar.svelte"; + import { onMount } from "svelte"; async function goHome() { goto("/"); } @@ -22,6 +23,15 @@ } let count = 0; + + // get value from fetch /api/visitcount + + onMount(async () => { + const res = await fetch("/api/visitcount"); + const data = await res.json(); + visitCount.set(data.visitCount); + }); + visitCount.subscribe((value) => { count = value; }); @@ -30,10 +40,6 @@ const isBrowser = typeof window !== "undefined"; if (isBrowser) { const storedAdventures = localStorage.getItem("adventures"); - if (storedAdventures) { - let parsed = JSON.parse(storedAdventures); - visitCount.set(parsed.length); - } } @@ -57,12 +63,12 @@ AdventureLog 🗺️