Creating recovery codes and starting to return to UI, adding seed data for initial admin user, and update dependencies.

This commit is contained in:
Bradley Shellnut 2024-04-04 15:08:18 -07:00
parent 8c5cda1ebc
commit 564c58a2c6
15 changed files with 1952 additions and 359 deletions

View file

@ -2,7 +2,8 @@
"useTabs": true, "useTabs": true,
"tabWidth": 2, "tabWidth": 2,
"singleQuote": true, "singleQuote": true,
"trailingComma": "none", "trailingComma": "all",
"bracketSpacing": true,
"printWidth": 100, "printWidth": 100,
"plugins": ["prettier-plugin-svelte"], "plugins": ["prettier-plugin-svelte"],
"pluginSearchDirs": ["."], "pluginSearchDirs": ["."],

View file

@ -0,0 +1,2 @@
ALTER TABLE "users" ALTER COLUMN "two_factor_secret" SET DEFAULT '';--> statement-breakpoint
ALTER TABLE "users" ADD COLUMN "two_factor_enabled" boolean DEFAULT false;

File diff suppressed because it is too large Load diff

View file

@ -64,6 +64,13 @@
"when": 1711757183163, "when": 1711757183163,
"tag": "0008_amusing_franklin_richards", "tag": "0008_amusing_franklin_richards",
"breakpoints": true "breakpoints": true
},
{
"idx": 9,
"version": "5",
"when": 1711868447607,
"tag": "0009_gray_carlie_cooper",
"breakpoints": true
} }
] ]
} }

View file

@ -24,13 +24,13 @@
"@melt-ui/pp": "^0.3.0", "@melt-ui/pp": "^0.3.0",
"@melt-ui/svelte": "^0.76.2", "@melt-ui/svelte": "^0.76.2",
"@playwright/test": "^1.42.1", "@playwright/test": "^1.42.1",
"@resvg/resvg-js": "^2.6.1", "@resvg/resvg-js": "^2.6.2",
"@sveltejs/adapter-auto": "^3.1.1", "@sveltejs/adapter-auto": "^3.2.0",
"@sveltejs/enhanced-img": "^0.1.9", "@sveltejs/enhanced-img": "^0.1.9",
"@sveltejs/kit": "^2.5.4", "@sveltejs/kit": "^2.5.5",
"@sveltejs/vite-plugin-svelte": "^3.0.2", "@sveltejs/vite-plugin-svelte": "^3.0.2",
"@types/cookie": "^0.6.0", "@types/cookie": "^0.6.0",
"@types/node": "^20.11.30", "@types/node": "^20.12.2",
"@types/pg": "^8.11.4", "@types/pg": "^8.11.4",
"@typescript-eslint/eslint-plugin": "^6.21.0", "@typescript-eslint/eslint-plugin": "^6.21.0",
"@typescript-eslint/parser": "^6.21.0", "@typescript-eslint/parser": "^6.21.0",
@ -59,13 +59,13 @@
"svelte-sequential-preprocessor": "^2.0.1", "svelte-sequential-preprocessor": "^2.0.1",
"sveltekit-flash-message": "^2.4.4", "sveltekit-flash-message": "^2.4.4",
"sveltekit-rate-limiter": "^0.4.3", "sveltekit-rate-limiter": "^0.4.3",
"sveltekit-superforms": "^2.11.0", "sveltekit-superforms": "^2.12.2",
"tailwindcss": "^3.4.1", "tailwindcss": "^3.4.3",
"ts-node": "^10.9.2", "ts-node": "^10.9.2",
"tslib": "^2.6.1", "tslib": "^2.6.1",
"tsx": "^4.7.1", "tsx": "^4.7.1",
"typescript": "^5.4.3", "typescript": "^5.4.3",
"vite": "^5.2.6", "vite": "^5.2.7",
"vitest": "^1.4.0", "vitest": "^1.4.0",
"zod": "^3.22.4" "zod": "^3.22.4"
}, },
@ -92,7 +92,7 @@
"class-variance-authority": "^0.7.0", "class-variance-authority": "^0.7.0",
"clsx": "^2.1.0", "clsx": "^2.1.0",
"cookie": "^0.6.0", "cookie": "^0.6.0",
"drizzle-orm": "^0.30.4", "drizzle-orm": "^0.30.6",
"feather-icons": "^4.29.1", "feather-icons": "^4.29.1",
"formsnap": "^0.5.1", "formsnap": "^0.5.1",
"html-entities": "^2.5.2", "html-entities": "^2.5.2",
@ -103,8 +103,8 @@
"lucia": "3.1.1", "lucia": "3.1.1",
"lucide-svelte": "^0.358.0", "lucide-svelte": "^0.358.0",
"open-props": "^1.6.21", "open-props": "^1.6.21",
"oslo": "^1.1.3", "oslo": "^1.2.0",
"pg": "^8.11.3", "pg": "^8.11.4",
"postgres": "^3.4.4", "postgres": "^3.4.4",
"qrcode": "^1.5.3", "qrcode": "^1.5.3",
"radix-svelte": "^0.9.0", "radix-svelte": "^0.9.0",

View file

@ -31,10 +31,10 @@ dependencies:
version: 1.16.0 version: 1.16.0
'@sentry/sveltekit': '@sentry/sveltekit':
specifier: ^7.100.1 specifier: ^7.100.1
version: 7.100.1(@sveltejs/kit@2.5.4)(svelte@4.2.12) version: 7.100.1(@sveltejs/kit@2.5.5)(svelte@4.2.12)
'@sveltejs/adapter-vercel': '@sveltejs/adapter-vercel':
specifier: ^5.2.0 specifier: ^5.2.0
version: 5.2.0(@sveltejs/kit@2.5.4) version: 5.2.0(@sveltejs/kit@2.5.5)
'@types/feather-icons': '@types/feather-icons':
specifier: ^4.29.4 specifier: ^4.29.4
version: 4.29.4 version: 4.29.4
@ -57,14 +57,14 @@ dependencies:
specifier: ^0.6.0 specifier: ^0.6.0
version: 0.6.0 version: 0.6.0
drizzle-orm: drizzle-orm:
specifier: ^0.30.4 specifier: ^0.30.6
version: 0.30.4(@neondatabase/serverless@0.9.0)(@planetscale/database@1.16.0)(@types/pg@8.11.4)(pg@8.11.3)(postgres@3.4.4) version: 0.30.6(@neondatabase/serverless@0.9.0)(@planetscale/database@1.16.0)(@types/pg@8.11.4)(pg@8.11.4)(postgres@3.4.4)
feather-icons: feather-icons:
specifier: ^4.29.1 specifier: ^4.29.1
version: 4.29.1 version: 4.29.1
formsnap: formsnap:
specifier: ^0.5.1 specifier: ^0.5.1
version: 0.5.1(svelte@4.2.12)(sveltekit-superforms@2.11.0) version: 0.5.1(svelte@4.2.12)(sveltekit-superforms@2.12.2)
html-entities: html-entities:
specifier: ^2.5.2 specifier: ^2.5.2
version: 2.5.2 version: 2.5.2
@ -90,11 +90,11 @@ dependencies:
specifier: ^1.6.21 specifier: ^1.6.21
version: 1.6.21 version: 1.6.21
oslo: oslo:
specifier: ^1.1.3 specifier: ^1.2.0
version: 1.1.3 version: 1.2.0
pg: pg:
specifier: ^8.11.3 specifier: ^8.11.4
version: 8.11.3 version: 8.11.4
postgres: postgres:
specifier: ^3.4.4 specifier: ^3.4.4
version: 3.4.4 version: 3.4.4
@ -115,10 +115,10 @@ dependencies:
version: 2.2.2 version: 2.2.2
tailwind-variants: tailwind-variants:
specifier: ^0.2.1 specifier: ^0.2.1
version: 0.2.1(tailwindcss@3.4.1) version: 0.2.1(tailwindcss@3.4.3)
tailwindcss-animate: tailwindcss-animate:
specifier: ^1.0.6 specifier: ^1.0.6
version: 1.0.7(tailwindcss@3.4.1) version: 1.0.7(tailwindcss@3.4.3)
zod-to-json-schema: zod-to-json-schema:
specifier: ^3.22.5 specifier: ^3.22.5
version: 3.22.5(zod@3.22.4) version: 3.22.5(zod@3.22.4)
@ -134,26 +134,26 @@ devDependencies:
specifier: ^1.42.1 specifier: ^1.42.1
version: 1.42.1 version: 1.42.1
'@resvg/resvg-js': '@resvg/resvg-js':
specifier: ^2.6.1 specifier: ^2.6.2
version: 2.6.1 version: 2.6.2
'@sveltejs/adapter-auto': '@sveltejs/adapter-auto':
specifier: ^3.1.1 specifier: ^3.2.0
version: 3.1.1(@sveltejs/kit@2.5.4) version: 3.2.0(@sveltejs/kit@2.5.5)
'@sveltejs/enhanced-img': '@sveltejs/enhanced-img':
specifier: ^0.1.9 specifier: ^0.1.9
version: 0.1.9(svelte@4.2.12) version: 0.1.9(svelte@4.2.12)
'@sveltejs/kit': '@sveltejs/kit':
specifier: ^2.5.4 specifier: ^2.5.5
version: 2.5.4(@sveltejs/vite-plugin-svelte@3.0.2)(svelte@4.2.12)(vite@5.2.6) version: 2.5.5(@sveltejs/vite-plugin-svelte@3.0.2)(svelte@4.2.12)(vite@5.2.7)
'@sveltejs/vite-plugin-svelte': '@sveltejs/vite-plugin-svelte':
specifier: ^3.0.2 specifier: ^3.0.2
version: 3.0.2(svelte@4.2.12)(vite@5.2.6) version: 3.0.2(svelte@4.2.12)(vite@5.2.7)
'@types/cookie': '@types/cookie':
specifier: ^0.6.0 specifier: ^0.6.0
version: 0.6.0 version: 0.6.0
'@types/node': '@types/node':
specifier: ^20.11.30 specifier: ^20.12.2
version: 20.11.30 version: 20.12.2
'@types/pg': '@types/pg':
specifier: ^8.11.4 specifier: ^8.11.4
version: 8.11.4 version: 8.11.4
@ -234,19 +234,19 @@ devDependencies:
version: 2.0.1 version: 2.0.1
sveltekit-flash-message: sveltekit-flash-message:
specifier: ^2.4.4 specifier: ^2.4.4
version: 2.4.4(@sveltejs/kit@2.5.4)(svelte@4.2.12) version: 2.4.4(@sveltejs/kit@2.5.5)(svelte@4.2.12)
sveltekit-rate-limiter: sveltekit-rate-limiter:
specifier: ^0.4.3 specifier: ^0.4.3
version: 0.4.3(@sveltejs/kit@2.5.4) version: 0.4.3(@sveltejs/kit@2.5.5)
sveltekit-superforms: sveltekit-superforms:
specifier: ^2.11.0 specifier: ^2.12.2
version: 2.11.0(@sveltejs/kit@2.5.4)(@types/json-schema@7.0.15)(esbuild-runner@2.2.2)(esbuild@0.20.2)(svelte@4.2.12) version: 2.12.2(@sveltejs/kit@2.5.5)(@types/json-schema@7.0.15)(esbuild-runner@2.2.2)(esbuild@0.20.2)(svelte@4.2.12)
tailwindcss: tailwindcss:
specifier: ^3.4.1 specifier: ^3.4.3
version: 3.4.1(ts-node@10.9.2) version: 3.4.3(ts-node@10.9.2)
ts-node: ts-node:
specifier: ^10.9.2 specifier: ^10.9.2
version: 10.9.2(@types/node@20.11.30)(typescript@5.4.3) version: 10.9.2(@types/node@20.12.2)(typescript@5.4.3)
tslib: tslib:
specifier: ^2.6.1 specifier: ^2.6.1
version: 2.6.2 version: 2.6.2
@ -257,11 +257,11 @@ devDependencies:
specifier: ^5.4.3 specifier: ^5.4.3
version: 5.4.3 version: 5.4.3
vite: vite:
specifier: ^5.2.6 specifier: ^5.2.7
version: 5.2.6(@types/node@20.11.30)(sass@1.72.0) version: 5.2.7(@types/node@20.12.2)(sass@1.72.0)
vitest: vitest:
specifier: ^1.4.0 specifier: ^1.4.0
version: 1.4.0(@types/node@20.11.30)(sass@1.72.0) version: 1.4.0(@types/node@20.12.2)(sass@1.72.0)
zod: zod:
specifier: ^3.22.4 specifier: ^3.22.4
version: 3.22.4 version: 3.22.4
@ -763,8 +763,8 @@ packages:
dev: false dev: false
optional: true optional: true
/@emnapi/core@1.1.0: /@emnapi/core@1.1.1:
resolution: {integrity: sha512-gNEVZo0HhUfVjhr6rFG//HZXbauclxueiDxaKGBZHcK5h8i9pslABNPfG8kMwYTubAn3mV7AyOZN8gfPRgbU8A==} resolution: {integrity: sha512-eu4KjHfXg3I+UUR7vSuwZXpRo4c8h4Rtb5Lu2F7Z4JqJFl/eidquONEBiRs6viXKpWBC3BaJBy68xGJ2j56idw==}
requiresBuild: true requiresBuild: true
dependencies: dependencies:
tslib: 2.6.2 tslib: 2.6.2
@ -778,8 +778,8 @@ packages:
tslib: 2.6.2 tslib: 2.6.2
optional: true optional: true
/@emnapi/runtime@1.1.0: /@emnapi/runtime@1.1.1:
resolution: {integrity: sha512-gCGlE0fJGWalfy+wbFApjhKn6uoSVvopru77IPyxNKkjkaiSx2HxDS7eOYSmo9dcMIhmmIvoxiC3N9TM1c3EaA==} resolution: {integrity: sha512-3bfqkzuR1KLx57nZfjr2NLnFOobvyS0aTszaEGCGqmYMVDRaGvgIZbjGSV/MHSSmLgQ/b9JFHQ5xm5WRZYd+XQ==}
requiresBuild: true requiresBuild: true
dependencies: dependencies:
tslib: 2.6.2 tslib: 2.6.2
@ -1674,8 +1674,8 @@ packages:
resolution: {integrity: sha512-1uFRjqCcxVv4F31PjyLm8o4oNlT5ywwh6OwcDGkZbafOeFZHXekvholS9IlfZkRsZvVhSbFRHT/5iDib4KTtpg==} resolution: {integrity: sha512-1uFRjqCcxVv4F31PjyLm8o4oNlT5ywwh6OwcDGkZbafOeFZHXekvholS9IlfZkRsZvVhSbFRHT/5iDib4KTtpg==}
dev: false dev: false
/@gcornut/valibot-json-schema@0.0.26(@types/json-schema@7.0.15)(esbuild-runner@2.2.2)(esbuild@0.20.2)(valibot@0.30.0): /@gcornut/valibot-json-schema@0.0.27(@types/json-schema@7.0.15)(esbuild-runner@2.2.2)(esbuild@0.20.2)(valibot@0.30.0):
resolution: {integrity: sha512-8eZpGLP1awX9UGEK+VUFiyXnjiIV/h5RPC7wt2KQG6O7YkXEw7t2pUxHhR5ULcoN0BsZ3FOWZqyzu3tFF7aXxw==} resolution: {integrity: sha512-xcMaUStVgQzPrK3d7PuLFbQ+3qSp6LzaLExAm52E3FKmUfjQa7Sw5cDK6Hfu/8WT0yfGsuSCuJ5uT1sosjR9Qg==}
hasBin: true hasBin: true
requiresBuild: true requiresBuild: true
peerDependencies: peerDependencies:
@ -2082,8 +2082,8 @@ packages:
resolution: {integrity: sha512-8JuczewTFIZ/XIjHQ+YlQUydHvlKx2hkcxtuGwh+t/t5zWyZct6YG4+xjHcq8xyc/e7FmFwf42Zj2YgICwmlvA==} resolution: {integrity: sha512-8JuczewTFIZ/XIjHQ+YlQUydHvlKx2hkcxtuGwh+t/t5zWyZct6YG4+xjHcq8xyc/e7FmFwf42Zj2YgICwmlvA==}
requiresBuild: true requiresBuild: true
dependencies: dependencies:
'@emnapi/core': 1.1.0 '@emnapi/core': 1.1.1
'@emnapi/runtime': 1.1.0 '@emnapi/runtime': 1.1.1
'@tybys/wasm-util': 0.8.1 '@tybys/wasm-util': 0.8.1
dev: false dev: false
optional: true optional: true
@ -2739,8 +2739,8 @@ packages:
requiresBuild: true requiresBuild: true
optional: true optional: true
/@resvg/resvg-js-android-arm-eabi@2.6.1: /@resvg/resvg-js-android-arm-eabi@2.6.2:
resolution: {integrity: sha512-oXmXUUqTzinvXwkVBDdNKocAeF1zLGJYasTNRmoqF3gyOm04qRYT1On0m6oK2jbTiUAOUTqi0ZSizcecnwcSDA==} resolution: {integrity: sha512-FrJibrAk6v29eabIPgcTUMPXiEz8ssrAk7TXxsiZzww9UTQ1Z5KAbFJs+Z0Ez+VZTYgnE5IQJqBcoSiMebtPHA==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [arm] cpu: [arm]
os: [android] os: [android]
@ -2748,8 +2748,8 @@ packages:
dev: true dev: true
optional: true optional: true
/@resvg/resvg-js-android-arm64@2.6.1: /@resvg/resvg-js-android-arm64@2.6.2:
resolution: {integrity: sha512-vcCZoBx8s/3/+t6nDd9fB/LL70I4B2YmgDT7uP6wyqVCUPniNeLR9VYIuvpMHw6oVyL5Mpt8F2YXV1zQE2X2hw==} resolution: {integrity: sha512-VcOKezEhm2VqzXpcIJoITuvUS/fcjIw5NA/w3tjzWyzmvoCdd+QXIqy3FBGulWdClvp4g+IfUemigrkLThSjAQ==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [arm64] cpu: [arm64]
os: [android] os: [android]
@ -2757,8 +2757,8 @@ packages:
dev: true dev: true
optional: true optional: true
/@resvg/resvg-js-darwin-arm64@2.6.1: /@resvg/resvg-js-darwin-arm64@2.6.2:
resolution: {integrity: sha512-uO0WvEQqQlAL8u7nI7k1yL5wSsZYU2YCSsN1hAhr1LjyvmWmyC09xUEdWPUVVT1nL2YK4Ueh0LR+pxOT3QlCng==} resolution: {integrity: sha512-nmok2LnAd6nLUKI16aEB9ydMC6Lidiiq2m1nEBDR1LaaP7FGs4AJ90qDraxX+CWlVuRlvNjyYJTNv8qFjtL9+A==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [arm64] cpu: [arm64]
os: [darwin] os: [darwin]
@ -2766,8 +2766,8 @@ packages:
dev: true dev: true
optional: true optional: true
/@resvg/resvg-js-darwin-x64@2.6.1: /@resvg/resvg-js-darwin-x64@2.6.2:
resolution: {integrity: sha512-aW15HMQSk85GPHE4gsc56G0Fqi2IGVkDfPWEWHEyDBpCZ17RKweAwg5V3ioz9aGX1nmhjQa9tJ2xgVwX+sqIjw==} resolution: {integrity: sha512-GInyZLjgWDfsVT6+SHxQVRwNzV0AuA1uqGsOAW+0th56J7Nh6bHHKXHBWzUrihxMetcFDmQMAX1tZ1fZDYSRsw==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [x64] cpu: [x64]
os: [darwin] os: [darwin]
@ -2775,8 +2775,8 @@ packages:
dev: true dev: true
optional: true optional: true
/@resvg/resvg-js-linux-arm-gnueabihf@2.6.1: /@resvg/resvg-js-linux-arm-gnueabihf@2.6.2:
resolution: {integrity: sha512-7vpBFzCMONnRzK0uCBT5h+Dmsa8dWsoLFqB6xgutNfKkldjuCOiLNe0tT7hneGF8tw5H+W6hX/VLx2ktDwsS4Q==} resolution: {integrity: sha512-YIV3u/R9zJbpqTTNwTZM5/ocWetDKGsro0SWp70eGEM9eV2MerWyBRZnQIgzU3YBnSBQ1RcxRZvY/UxwESfZIw==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [arm] cpu: [arm]
os: [linux] os: [linux]
@ -2784,8 +2784,8 @@ packages:
dev: true dev: true
optional: true optional: true
/@resvg/resvg-js-linux-arm64-gnu@2.6.1: /@resvg/resvg-js-linux-arm64-gnu@2.6.2:
resolution: {integrity: sha512-+Gi3OIOJFFiCdm72AsDa7KPnkogitLQ6yfF1O/J25adUrlWjvKAM9+8b5sTI9waeLERZHNJpIVESpdIxI2/5sQ==} resolution: {integrity: sha512-zc2BlJSim7YR4FZDQ8OUoJg5holYzdiYMeobb9pJuGDidGL9KZUv7SbiD4E8oZogtYY42UZEap7dqkkYuA91pg==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
@ -2793,8 +2793,8 @@ packages:
dev: true dev: true
optional: true optional: true
/@resvg/resvg-js-linux-arm64-musl@2.6.1: /@resvg/resvg-js-linux-arm64-musl@2.6.2:
resolution: {integrity: sha512-lnRGWG/LwpX6UsV1neHAavPnek3WlCnGMdBZ/7JbpamK5VmtDZmsV2esOFpME6pKnWL40UX4WjPsCBtKkhMUMA==} resolution: {integrity: sha512-3h3dLPWNgSsD4lQBJPb4f+kvdOSJHa5PjTYVsWHxLUzH4IFTJUAnmuWpw4KqyQ3NA5QCyhw4TWgxk3jRkQxEKg==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
@ -2802,8 +2802,8 @@ packages:
dev: true dev: true
optional: true optional: true
/@resvg/resvg-js-linux-x64-gnu@2.6.1: /@resvg/resvg-js-linux-x64-gnu@2.6.2:
resolution: {integrity: sha512-2S1N7fHl5480AUrUtxsfjFOh3t8NQ2qKavROZRDKWJqFXBrNOUsirDa33LtpFGDkFW18SjP/FCs1xfHLzzz43g==} resolution: {integrity: sha512-IVUe+ckIerA7xMZ50duAZzwf1U7khQe2E0QpUxu5MBJNao5RqC0zwV/Zm965vw6D3gGFUl7j4m+oJjubBVoftw==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
@ -2811,8 +2811,8 @@ packages:
dev: true dev: true
optional: true optional: true
/@resvg/resvg-js-linux-x64-musl@2.6.1: /@resvg/resvg-js-linux-x64-musl@2.6.2:
resolution: {integrity: sha512-G5GMmpvFiyclkp44eVPVWnN2lhLx9eMIcxGnBFWjnpI3TxrjBt7aVic2N8CsZ0vt2rrnJkQI0IKjUQQIi138Hw==} resolution: {integrity: sha512-UOf83vqTzoYQO9SZ0fPl2ZIFtNIz/Rr/y+7X8XRX1ZnBYsQ/tTb+cj9TE+KHOdmlTFBxhYzVkP2lRByCzqi4jQ==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
@ -2820,8 +2820,8 @@ packages:
dev: true dev: true
optional: true optional: true
/@resvg/resvg-js-win32-arm64-msvc@2.6.1: /@resvg/resvg-js-win32-arm64-msvc@2.6.2:
resolution: {integrity: sha512-m9TBMrGs2tML0oz14D/x40tPedqCgNFy/DH7/z/bvnP9bH59fivaInmHTjd8oSFfGRZ/DasXMFcAL+LS1+hfSQ==} resolution: {integrity: sha512-7C/RSgCa+7vqZ7qAbItfiaAWhyRSoD4l4BQAbVDqRRsRgY+S+hgS3in0Rxr7IorKUpGE69X48q6/nOAuTJQxeQ==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [arm64] cpu: [arm64]
os: [win32] os: [win32]
@ -2829,8 +2829,8 @@ packages:
dev: true dev: true
optional: true optional: true
/@resvg/resvg-js-win32-ia32-msvc@2.6.1: /@resvg/resvg-js-win32-ia32-msvc@2.6.2:
resolution: {integrity: sha512-Ma+MJXesViT0A7JqTobsB9DOCO0AkfmLxsgkvxq0IiWkpU9Z4Gp+RkDsFQbMhJwVXaz7b8L6y+EIvf95iCbJQw==} resolution: {integrity: sha512-har4aPAlvjnLcil40AC77YDIk6loMawuJwFINEM7n0pZviwMkMvjb2W5ZirsNOZY4aDbo5tLx0wNMREp5Brk+w==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [ia32] cpu: [ia32]
os: [win32] os: [win32]
@ -2838,8 +2838,8 @@ packages:
dev: true dev: true
optional: true optional: true
/@resvg/resvg-js-win32-x64-msvc@2.6.1: /@resvg/resvg-js-win32-x64-msvc@2.6.2:
resolution: {integrity: sha512-mWIlgEuFWBrlldCbhLPvG4tt0r0D1RZ8eR2+zxTtQ15d/lbVjwnGpw2l1noV3zhV5S6mAVzoZeQ1emoov63Y/A==} resolution: {integrity: sha512-ZXtYhtUr5SSaBrUDq7DiyjOFJqBVL/dOBN7N/qmi/pO0IgiWW/f/ue3nbvu9joWE5aAKDoIzy/CxsY0suwGosQ==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [x64] cpu: [x64]
os: [win32] os: [win32]
@ -2847,22 +2847,22 @@ packages:
dev: true dev: true
optional: true optional: true
/@resvg/resvg-js@2.6.1: /@resvg/resvg-js@2.6.2:
resolution: {integrity: sha512-CVGUWPvgr3b96+PooncXCsvu93CMwfEoueqIxIJr9AuUA8OaIPcZjOgFhas62fcHRdWMmxQqF1Rp+89bQsj/RA==} resolution: {integrity: sha512-xBaJish5OeGmniDj9cW5PRa/PtmuVU3ziqrbr5xJj901ZDN4TosrVaNZpEiLZAxdfnhAe7uQ7QFWfjPe9d9K2Q==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
optionalDependencies: optionalDependencies:
'@resvg/resvg-js-android-arm-eabi': 2.6.1 '@resvg/resvg-js-android-arm-eabi': 2.6.2
'@resvg/resvg-js-android-arm64': 2.6.1 '@resvg/resvg-js-android-arm64': 2.6.2
'@resvg/resvg-js-darwin-arm64': 2.6.1 '@resvg/resvg-js-darwin-arm64': 2.6.2
'@resvg/resvg-js-darwin-x64': 2.6.1 '@resvg/resvg-js-darwin-x64': 2.6.2
'@resvg/resvg-js-linux-arm-gnueabihf': 2.6.1 '@resvg/resvg-js-linux-arm-gnueabihf': 2.6.2
'@resvg/resvg-js-linux-arm64-gnu': 2.6.1 '@resvg/resvg-js-linux-arm64-gnu': 2.6.2
'@resvg/resvg-js-linux-arm64-musl': 2.6.1 '@resvg/resvg-js-linux-arm64-musl': 2.6.2
'@resvg/resvg-js-linux-x64-gnu': 2.6.1 '@resvg/resvg-js-linux-x64-gnu': 2.6.2
'@resvg/resvg-js-linux-x64-musl': 2.6.1 '@resvg/resvg-js-linux-x64-musl': 2.6.2
'@resvg/resvg-js-win32-arm64-msvc': 2.6.1 '@resvg/resvg-js-win32-arm64-msvc': 2.6.2
'@resvg/resvg-js-win32-ia32-msvc': 2.6.1 '@resvg/resvg-js-win32-ia32-msvc': 2.6.2
'@resvg/resvg-js-win32-x64-msvc': 2.6.1 '@resvg/resvg-js-win32-x64-msvc': 2.6.2
dev: true dev: true
/@resvg/resvg-wasm@2.6.0: /@resvg/resvg-wasm@2.6.0:
@ -3126,7 +3126,7 @@ packages:
svelte: 4.2.12 svelte: 4.2.12
dev: false dev: false
/@sentry/sveltekit@7.100.1(@sveltejs/kit@2.5.4)(svelte@4.2.12): /@sentry/sveltekit@7.100.1(@sveltejs/kit@2.5.5)(svelte@4.2.12):
resolution: {integrity: sha512-t6JaivTmw5oIqOpKQ8PNbGjNP99AQY6vMPkhxzVuwPa3A3o2WtmzQoIXNxdrkux5XkoBI9CsT6TsM5TbaMDwjQ==} resolution: {integrity: sha512-t6JaivTmw5oIqOpKQ8PNbGjNP99AQY6vMPkhxzVuwPa3A3o2WtmzQoIXNxdrkux5XkoBI9CsT6TsM5TbaMDwjQ==}
engines: {node: '>=16'} engines: {node: '>=16'}
peerDependencies: peerDependencies:
@ -3140,7 +3140,7 @@ packages:
'@sentry/types': 7.100.1 '@sentry/types': 7.100.1
'@sentry/utils': 7.100.1 '@sentry/utils': 7.100.1
'@sentry/vite-plugin': 0.6.1 '@sentry/vite-plugin': 0.6.1
'@sveltejs/kit': 2.5.4(@sveltejs/vite-plugin-svelte@3.0.2)(svelte@4.2.12)(vite@5.2.6) '@sveltejs/kit': 2.5.5(@sveltejs/vite-plugin-svelte@3.0.2)(svelte@4.2.12)(vite@5.2.7)
magicast: 0.2.8 magicast: 0.2.8
sorcery: 0.11.0 sorcery: 0.11.0
transitivePeerDependencies: transitivePeerDependencies:
@ -3229,21 +3229,21 @@ packages:
requiresBuild: true requiresBuild: true
optional: true optional: true
/@sveltejs/adapter-auto@3.1.1(@sveltejs/kit@2.5.4): /@sveltejs/adapter-auto@3.2.0(@sveltejs/kit@2.5.5):
resolution: {integrity: sha512-6LeZft2Fo/4HfmLBi5CucMYmgRxgcETweQl/yQoZo/895K3S9YWYN4Sfm/IhwlIpbJp3QNvhKmwCHbsqQNYQpw==} resolution: {integrity: sha512-She5nKT47kwHE18v9NMe6pbJcvULr82u0V3yZ0ej3n1laWKGgkgdEABE9/ak5iDPs93LqsBkuIo51kkwCLBjJA==}
peerDependencies: peerDependencies:
'@sveltejs/kit': ^2.0.0 '@sveltejs/kit': ^2.0.0
dependencies: dependencies:
'@sveltejs/kit': 2.5.4(@sveltejs/vite-plugin-svelte@3.0.2)(svelte@4.2.12)(vite@5.2.6) '@sveltejs/kit': 2.5.5(@sveltejs/vite-plugin-svelte@3.0.2)(svelte@4.2.12)(vite@5.2.7)
import-meta-resolve: 4.0.0 import-meta-resolve: 4.0.0
dev: true dev: true
/@sveltejs/adapter-vercel@5.2.0(@sveltejs/kit@2.5.4): /@sveltejs/adapter-vercel@5.2.0(@sveltejs/kit@2.5.5):
resolution: {integrity: sha512-872y13DxKcOBxgnXc4C2YHRw1ow9N1CpUxMH34NYFqCn6PUO6f34qle8v/Byr8sHEC/d+PZIAI3MJs3c8f7TfA==} resolution: {integrity: sha512-872y13DxKcOBxgnXc4C2YHRw1ow9N1CpUxMH34NYFqCn6PUO6f34qle8v/Byr8sHEC/d+PZIAI3MJs3c8f7TfA==}
peerDependencies: peerDependencies:
'@sveltejs/kit': ^2.4.0 '@sveltejs/kit': ^2.4.0
dependencies: dependencies:
'@sveltejs/kit': 2.5.4(@sveltejs/vite-plugin-svelte@3.0.2)(svelte@4.2.12)(vite@5.2.6) '@sveltejs/kit': 2.5.5(@sveltejs/vite-plugin-svelte@3.0.2)(svelte@4.2.12)(vite@5.2.7)
'@vercel/nft': 0.26.4 '@vercel/nft': 0.26.4
esbuild: 0.19.12 esbuild: 0.19.12
transitivePeerDependencies: transitivePeerDependencies:
@ -3262,8 +3262,8 @@ packages:
- svelte - svelte
dev: true dev: true
/@sveltejs/kit@2.5.4(@sveltejs/vite-plugin-svelte@3.0.2)(svelte@4.2.12)(vite@5.2.6): /@sveltejs/kit@2.5.5(@sveltejs/vite-plugin-svelte@3.0.2)(svelte@4.2.12)(vite@5.2.7):
resolution: {integrity: sha512-eDxK2d4EGzk99QsZNoPXe7jlzA5EGqfcCpUwZ912bhnalsZ2ZsG5wGRthkydupVjYyqdmzEanVKFhLxU2vkPSQ==} resolution: {integrity: sha512-ULe3PB00q4+wYRL+IS5FDPsCEVnhEITofm7b9Yz8malcH3r1SAnW/JJ6T13hIMeu8QNRIuVQWo+P4+2VklbnLQ==}
engines: {node: '>=18.13'} engines: {node: '>=18.13'}
hasBin: true hasBin: true
requiresBuild: true requiresBuild: true
@ -3272,7 +3272,7 @@ packages:
svelte: ^4.0.0 || ^5.0.0-next.0 svelte: ^4.0.0 || ^5.0.0-next.0
vite: ^5.0.3 vite: ^5.0.3
dependencies: dependencies:
'@sveltejs/vite-plugin-svelte': 3.0.2(svelte@4.2.12)(vite@5.2.6) '@sveltejs/vite-plugin-svelte': 3.0.2(svelte@4.2.12)(vite@5.2.7)
'@types/cookie': 0.6.0 '@types/cookie': 0.6.0
cookie: 0.6.0 cookie: 0.6.0
devalue: 4.3.2 devalue: 4.3.2
@ -3286,9 +3286,9 @@ packages:
sirv: 2.0.4 sirv: 2.0.4
svelte: 4.2.12 svelte: 4.2.12
tiny-glob: 0.2.9 tiny-glob: 0.2.9
vite: 5.2.6(@types/node@20.11.30)(sass@1.72.0) vite: 5.2.7(@types/node@20.12.2)(sass@1.72.0)
/@sveltejs/vite-plugin-svelte-inspector@2.0.0(@sveltejs/vite-plugin-svelte@3.0.2)(svelte@4.2.12)(vite@5.2.6): /@sveltejs/vite-plugin-svelte-inspector@2.0.0(@sveltejs/vite-plugin-svelte@3.0.2)(svelte@4.2.12)(vite@5.2.7):
resolution: {integrity: sha512-gjr9ZFg1BSlIpfZ4PRewigrvYmHWbDrq2uvvPB1AmTWKuM+dI1JXQSUu2pIrYLb/QncyiIGkFDFKTwJ0XqQZZg==} resolution: {integrity: sha512-gjr9ZFg1BSlIpfZ4PRewigrvYmHWbDrq2uvvPB1AmTWKuM+dI1JXQSUu2pIrYLb/QncyiIGkFDFKTwJ0XqQZZg==}
engines: {node: ^18.0.0 || >=20} engines: {node: ^18.0.0 || >=20}
peerDependencies: peerDependencies:
@ -3296,29 +3296,29 @@ packages:
svelte: ^4.0.0 || ^5.0.0-next.0 svelte: ^4.0.0 || ^5.0.0-next.0
vite: ^5.0.0 vite: ^5.0.0
dependencies: dependencies:
'@sveltejs/vite-plugin-svelte': 3.0.2(svelte@4.2.12)(vite@5.2.6) '@sveltejs/vite-plugin-svelte': 3.0.2(svelte@4.2.12)(vite@5.2.7)
debug: 4.3.4 debug: 4.3.4
svelte: 4.2.12 svelte: 4.2.12
vite: 5.2.6(@types/node@20.11.30)(sass@1.72.0) vite: 5.2.7(@types/node@20.12.2)(sass@1.72.0)
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
/@sveltejs/vite-plugin-svelte@3.0.2(svelte@4.2.12)(vite@5.2.6): /@sveltejs/vite-plugin-svelte@3.0.2(svelte@4.2.12)(vite@5.2.7):
resolution: {integrity: sha512-MpmF/cju2HqUls50WyTHQBZUV3ovV/Uk8k66AN2gwHogNAG8wnW8xtZDhzNBsFJJuvmq1qnzA5kE7YfMJNFv2Q==} resolution: {integrity: sha512-MpmF/cju2HqUls50WyTHQBZUV3ovV/Uk8k66AN2gwHogNAG8wnW8xtZDhzNBsFJJuvmq1qnzA5kE7YfMJNFv2Q==}
engines: {node: ^18.0.0 || >=20} engines: {node: ^18.0.0 || >=20}
peerDependencies: peerDependencies:
svelte: ^4.0.0 || ^5.0.0-next.0 svelte: ^4.0.0 || ^5.0.0-next.0
vite: ^5.0.0 vite: ^5.0.0
dependencies: dependencies:
'@sveltejs/vite-plugin-svelte-inspector': 2.0.0(@sveltejs/vite-plugin-svelte@3.0.2)(svelte@4.2.12)(vite@5.2.6) '@sveltejs/vite-plugin-svelte-inspector': 2.0.0(@sveltejs/vite-plugin-svelte@3.0.2)(svelte@4.2.12)(vite@5.2.7)
debug: 4.3.4 debug: 4.3.4
deepmerge: 4.3.1 deepmerge: 4.3.1
kleur: 4.1.5 kleur: 4.1.5
magic-string: 0.30.5 magic-string: 0.30.5
svelte: 4.2.12 svelte: 4.2.12
svelte-hmr: 0.15.3(svelte@4.2.12) svelte-hmr: 0.15.3(svelte@4.2.12)
vite: 5.2.6(@types/node@20.11.30)(sass@1.72.0) vite: 5.2.7(@types/node@20.12.2)(sass@1.72.0)
vitefu: 0.2.5(vite@5.2.6) vitefu: 0.2.5(vite@5.2.7)
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
@ -3360,22 +3360,22 @@ packages:
/@types/json-schema@7.0.15: /@types/json-schema@7.0.15:
resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
/@types/node@20.11.30: /@types/node@20.12.2:
resolution: {integrity: sha512-dHM6ZxwlmuZaRmUPfv1p+KrdD1Dci04FbdEm/9wEMouFqxYoFl5aMkt0VMAUtYRQDyYvD41WJLukhq/ha3YuTw==} resolution: {integrity: sha512-zQ0NYO87hyN6Xrclcqp7f8ZbXNbRfoGWNcMvHTPQp9UUrwI0mI7XBz+cu7/W6/VClYo2g63B0cjull/srU7LgQ==}
dependencies: dependencies:
undici-types: 5.26.5 undici-types: 5.26.5
/@types/pg@8.11.4: /@types/pg@8.11.4:
resolution: {integrity: sha512-yw3Bwbda6vO+NvI1Ue/YKOwtl31AYvvd/e73O3V4ZkNzuGpTDndLSyc0dQRB2xrQqDePd20pEGIfqSp/GH3pRw==} resolution: {integrity: sha512-yw3Bwbda6vO+NvI1Ue/YKOwtl31AYvvd/e73O3V4ZkNzuGpTDndLSyc0dQRB2xrQqDePd20pEGIfqSp/GH3pRw==}
dependencies: dependencies:
'@types/node': 20.11.30 '@types/node': 20.12.2
pg-protocol: 1.6.0 pg-protocol: 1.6.0
pg-types: 4.0.2 pg-types: 4.0.2
/@types/pg@8.6.6: /@types/pg@8.6.6:
resolution: {integrity: sha512-O2xNmXebtwVekJDD+02udOncjVcMZQuTEQEMpKJ0ZRf5E7/9JJX3izhKUcUifBkyKpljyUM6BTgy2trmviKlpw==} resolution: {integrity: sha512-O2xNmXebtwVekJDD+02udOncjVcMZQuTEQEMpKJ0ZRf5E7/9JJX3izhKUcUifBkyKpljyUM6BTgy2trmviKlpw==}
dependencies: dependencies:
'@types/node': 20.11.30 '@types/node': 20.12.2
pg-protocol: 1.6.0 pg-protocol: 1.6.0
pg-types: 2.2.0 pg-types: 2.2.0
dev: false dev: false
@ -3863,11 +3863,6 @@ packages:
/buffer-from@1.1.2: /buffer-from@1.1.2:
resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
/buffer-writer@2.0.0:
resolution: {integrity: sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==}
engines: {node: '>=4'}
dev: false
/cac@6.7.14: /cac@6.7.14:
resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==}
engines: {node: '>=8'} engines: {node: '>=8'}
@ -4317,11 +4312,12 @@ packages:
- supports-color - supports-color
dev: true dev: true
/drizzle-orm@0.30.4(@neondatabase/serverless@0.9.0)(@planetscale/database@1.16.0)(@types/pg@8.11.4)(pg@8.11.3)(postgres@3.4.4): /drizzle-orm@0.30.6(@neondatabase/serverless@0.9.0)(@planetscale/database@1.16.0)(@types/pg@8.11.4)(pg@8.11.4)(postgres@3.4.4):
resolution: {integrity: sha512-kWoSMGbrOFkmkAweLTFtHJMpN+nwhx89q0mLELqT2aEU+1szNV8jrnBmJwZ0WGNp7J7yQn/ezEtxBI/qzTSElQ==} resolution: {integrity: sha512-8RgNUmY7J03GRuRgBV5SaJNbYgLVPjdSWNS/bRkIMIHt2TFCA439lJsNpqYX8asyKMqkw8ceBiamUnCIXZIt9w==}
peerDependencies: peerDependencies:
'@aws-sdk/client-rds-data': '>=3' '@aws-sdk/client-rds-data': '>=3'
'@cloudflare/workers-types': '>=3' '@cloudflare/workers-types': '>=3'
'@electric-sql/pglite': '>=0.1.1'
'@libsql/client': '*' '@libsql/client': '*'
'@neondatabase/serverless': '>=0.1' '@neondatabase/serverless': '>=0.1'
'@op-engineering/op-sqlite': '>=2' '@op-engineering/op-sqlite': '>=2'
@ -4349,6 +4345,8 @@ packages:
optional: true optional: true
'@cloudflare/workers-types': '@cloudflare/workers-types':
optional: true optional: true
'@electric-sql/pglite':
optional: true
'@libsql/client': '@libsql/client':
optional: true optional: true
'@neondatabase/serverless': '@neondatabase/serverless':
@ -4397,7 +4395,7 @@ packages:
'@neondatabase/serverless': 0.9.0 '@neondatabase/serverless': 0.9.0
'@planetscale/database': 1.16.0 '@planetscale/database': 1.16.0
'@types/pg': 8.11.4 '@types/pg': 8.11.4
pg: 8.11.3 pg: 8.11.4
postgres: 3.4.4 postgres: 3.4.4
dev: false dev: false
@ -4900,7 +4898,7 @@ packages:
is-callable: 1.2.7 is-callable: 1.2.7
dev: false dev: false
/formsnap@0.5.1(svelte@4.2.12)(sveltekit-superforms@2.11.0): /formsnap@0.5.1(svelte@4.2.12)(sveltekit-superforms@2.12.2):
resolution: {integrity: sha512-8ppOlOu7llBEJbV0PzUz/KWh1J8KfiGqwjiyb8emQ2m+/nYXohLBtMcLVpW3XwlMkUbYaIXM+5lhfGjw8xbGJw==} resolution: {integrity: sha512-8ppOlOu7llBEJbV0PzUz/KWh1J8KfiGqwjiyb8emQ2m+/nYXohLBtMcLVpW3XwlMkUbYaIXM+5lhfGjw8xbGJw==}
peerDependencies: peerDependencies:
svelte: ^4.0.0 svelte: ^4.0.0
@ -4908,7 +4906,7 @@ packages:
dependencies: dependencies:
nanoid: 5.0.6 nanoid: 5.0.6
svelte: 4.2.12 svelte: 4.2.12
sveltekit-superforms: 2.11.0(@sveltejs/kit@2.5.4)(@types/json-schema@7.0.15)(esbuild-runner@2.2.2)(esbuild@0.20.2)(svelte@4.2.12) sveltekit-superforms: 2.12.2(@sveltejs/kit@2.5.5)(@types/json-schema@7.0.15)(esbuild-runner@2.2.2)(esbuild@0.20.2)(svelte@4.2.12)
dev: false dev: false
/fraction.js@4.3.7: /fraction.js@4.3.7:
@ -5876,8 +5874,8 @@ packages:
'@node-rs/bcrypt': 1.9.2 '@node-rs/bcrypt': 1.9.2
dev: false dev: false
/oslo@1.1.3: /oslo@1.2.0:
resolution: {integrity: sha512-hCz528UlNTiegplcyBg6AvG0HLNrnq06EJMp88Ze308GX1hszkb8u3puhNC4aqLMbYQ0hXpl+wQGnwxMtt5+5w==} resolution: {integrity: sha512-OoFX6rDsNcOQVAD2gQD/z03u4vEjWZLzJtwkmgfRF+KpQUXwdgEXErD7zNhyowmHwHefP+PM9Pw13pgpHMRlzw==}
dependencies: dependencies:
'@node-rs/argon2': 1.7.0 '@node-rs/argon2': 1.7.0
'@node-rs/bcrypt': 1.9.0 '@node-rs/bcrypt': 1.9.0
@ -5921,10 +5919,6 @@ packages:
engines: {node: '>=6'} engines: {node: '>=6'}
dev: false dev: false
/packet-reader@1.0.0:
resolution: {integrity: sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==}
dev: false
/pako@0.2.9: /pako@0.2.9:
resolution: {integrity: sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==} resolution: {integrity: sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==}
@ -5996,8 +5990,8 @@ packages:
dev: false dev: false
optional: true optional: true
/pg-connection-string@2.6.2: /pg-connection-string@2.6.3:
resolution: {integrity: sha512-ch6OwaeaPYcova4kKZ15sbJ2hKb/VP48ZD2gE7i1J+L4MspCtBMAx8nMgz7bksc7IojCIIWuEhHibSMFH8m8oA==} resolution: {integrity: sha512-77FxhhKJQH+xJx6tDqkhhMa0nZvv3U1HYLDQgwZxZafVD583++O5LXn5oo5HaQZ0vXwYcZA1koYAJM3JvD6Gtw==}
dev: false dev: false
/pg-int8@1.0.1: /pg-int8@1.0.1:
@ -6008,17 +6002,21 @@ packages:
resolution: {integrity: sha512-BM/Thnrw5jm2kKLE5uJkXqqExRUY/toLHda65XgFTBTFYZyopbKjBe29Ii3RbkvlsMoFwD+tHeGaCjjv0gHlyw==} resolution: {integrity: sha512-BM/Thnrw5jm2kKLE5uJkXqqExRUY/toLHda65XgFTBTFYZyopbKjBe29Ii3RbkvlsMoFwD+tHeGaCjjv0gHlyw==}
engines: {node: '>=4'} engines: {node: '>=4'}
/pg-pool@3.6.1(pg@8.11.3): /pg-pool@3.6.2(pg@8.11.4):
resolution: {integrity: sha512-jizsIzhkIitxCGfPRzJn1ZdcosIt3pz9Sh3V01fm1vZnbnCMgmGl5wvGGdNN2EL9Rmb0EcFoCkixH4Pu+sP9Og==} resolution: {integrity: sha512-Htjbg8BlwXqSBQ9V8Vjtc+vzf/6fVUuak/3/XXKA9oxZprwW3IMDQTGHP+KDmVL7rtd+R1QjbnCFPuTHm3G4hg==}
peerDependencies: peerDependencies:
pg: '>=8.0' pg: '>=8.0'
dependencies: dependencies:
pg: 8.11.3 pg: 8.11.4
dev: false dev: false
/pg-protocol@1.6.0: /pg-protocol@1.6.0:
resolution: {integrity: sha512-M+PDm637OY5WM307051+bsDia5Xej6d9IR4GwJse1qA1DIhiKlksvrneZOYQq42OM+spubpcNYEo2FcKQrDk+Q==} resolution: {integrity: sha512-M+PDm637OY5WM307051+bsDia5Xej6d9IR4GwJse1qA1DIhiKlksvrneZOYQq42OM+spubpcNYEo2FcKQrDk+Q==}
/pg-protocol@1.6.1:
resolution: {integrity: sha512-jPIlvgoD63hrEuihvIg+tJhoGjUsLPn6poJY9N5CnlPd91c2T18T/9zBtLxZSb1EhYxBRoZJtzScCaWlYLtktg==}
dev: false
/pg-types@2.2.0: /pg-types@2.2.0:
resolution: {integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==} resolution: {integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==}
engines: {node: '>=4'} engines: {node: '>=4'}
@ -6042,8 +6040,8 @@ packages:
postgres-interval: 3.0.0 postgres-interval: 3.0.0
postgres-range: 1.1.4 postgres-range: 1.1.4
/pg@8.11.3: /pg@8.11.4:
resolution: {integrity: sha512-+9iuvG8QfaaUrrph+kpF24cXkH1YOOUeArRNYIxq1viYHZagBxrTno7cecY1Fa44tJeZvaoG+Djpkc3JwehN5g==} resolution: {integrity: sha512-pWb7JKPxGk1UFbtq7jQ0m3IfPpb7LLACCEyN8/u9DYEom+Q/BSKy+4TRl4+Hh003AOYhppB/z+QK87/hx/bk0w==}
engines: {node: '>= 8.0.0'} engines: {node: '>= 8.0.0'}
peerDependencies: peerDependencies:
pg-native: '>=3.0.1' pg-native: '>=3.0.1'
@ -6051,11 +6049,9 @@ packages:
pg-native: pg-native:
optional: true optional: true
dependencies: dependencies:
buffer-writer: 2.0.0 pg-connection-string: 2.6.3
packet-reader: 1.0.0 pg-pool: 3.6.2(pg@8.11.4)
pg-connection-string: 2.6.2 pg-protocol: 1.6.1
pg-pool: 3.6.1(pg@8.11.3)
pg-protocol: 1.6.0
pg-types: 2.2.0 pg-types: 2.2.0
pgpass: 1.0.5 pgpass: 1.0.5
optionalDependencies: optionalDependencies:
@ -6338,7 +6334,7 @@ packages:
dependencies: dependencies:
lilconfig: 2.1.0 lilconfig: 2.1.0
postcss: 8.4.38 postcss: 8.4.38
ts-node: 10.9.2(@types/node@20.11.30)(typescript@5.4.3) ts-node: 10.9.2(@types/node@20.12.2)(typescript@5.4.3)
yaml: 1.10.2 yaml: 1.10.2
dev: true dev: true
@ -6356,7 +6352,7 @@ packages:
dependencies: dependencies:
lilconfig: 3.0.0 lilconfig: 3.0.0
postcss: 8.4.38 postcss: 8.4.38
ts-node: 10.9.2(@types/node@20.11.30)(typescript@5.4.3) ts-node: 10.9.2(@types/node@20.12.2)(typescript@5.4.3)
yaml: 2.3.4 yaml: 2.3.4
/postcss-load-config@5.0.3(postcss@8.4.38): /postcss-load-config@5.0.3(postcss@8.4.38):
@ -7345,56 +7341,39 @@ packages:
magic-string: 0.30.7 magic-string: 0.30.7
periscopic: 3.1.0 periscopic: 3.1.0
/sveltekit-flash-message@2.4.4(@sveltejs/kit@2.5.4)(svelte@4.2.12): /sveltekit-flash-message@2.4.4(@sveltejs/kit@2.5.5)(svelte@4.2.12):
resolution: {integrity: sha512-CFN03chH/FMEJcBZ/8zKm7RqGee/pwb57Spbbx8QCQPhe7N9ofZHd9iYV2vVy4E9glBo/oQ1IG7VQje6L092wg==} resolution: {integrity: sha512-CFN03chH/FMEJcBZ/8zKm7RqGee/pwb57Spbbx8QCQPhe7N9ofZHd9iYV2vVy4E9glBo/oQ1IG7VQje6L092wg==}
peerDependencies: peerDependencies:
'@sveltejs/kit': 1.x || 2.x '@sveltejs/kit': 1.x || 2.x
svelte: 3.x || 4.x || >=5.0.0-next.51 svelte: 3.x || 4.x || >=5.0.0-next.51
dependencies: dependencies:
'@sveltejs/kit': 2.5.4(@sveltejs/vite-plugin-svelte@3.0.2)(svelte@4.2.12)(vite@5.2.6) '@sveltejs/kit': 2.5.5(@sveltejs/vite-plugin-svelte@3.0.2)(svelte@4.2.12)(vite@5.2.7)
svelte: 4.2.12 svelte: 4.2.12
dev: true dev: true
/sveltekit-rate-limiter@0.4.3(@sveltejs/kit@2.5.4): /sveltekit-rate-limiter@0.4.3(@sveltejs/kit@2.5.5):
resolution: {integrity: sha512-BKkD2tvgyz5j4Fn1vt0y7FLF0zZ01f9thjWPGDb6fyX3tBXyMrtZ8ISK8M7zjz9Cik/2KrkvFtmldhXF6/hjqw==} resolution: {integrity: sha512-BKkD2tvgyz5j4Fn1vt0y7FLF0zZ01f9thjWPGDb6fyX3tBXyMrtZ8ISK8M7zjz9Cik/2KrkvFtmldhXF6/hjqw==}
peerDependencies: peerDependencies:
'@sveltejs/kit': 1.x || 2.x '@sveltejs/kit': 1.x || 2.x
dependencies: dependencies:
'@isaacs/ttlcache': 1.4.1 '@isaacs/ttlcache': 1.4.1
'@sveltejs/kit': 2.5.4(@sveltejs/vite-plugin-svelte@3.0.2)(svelte@4.2.12)(vite@5.2.6) '@sveltejs/kit': 2.5.5(@sveltejs/vite-plugin-svelte@3.0.2)(svelte@4.2.12)(vite@5.2.7)
dev: true dev: true
/sveltekit-superforms@2.11.0(@sveltejs/kit@2.5.4)(@types/json-schema@7.0.15)(esbuild-runner@2.2.2)(esbuild@0.20.2)(svelte@4.2.12): /sveltekit-superforms@2.12.2(@sveltejs/kit@2.5.5)(@types/json-schema@7.0.15)(esbuild-runner@2.2.2)(esbuild@0.20.2)(svelte@4.2.12):
resolution: {integrity: sha512-wRAznfYi9sOp4aQd2kb/SIafqHX4LROn5ojIXEbp2Pss9EPy69tmArQFm3JaiBC0hU72bAlUOTFSmVABTF9TEA==} resolution: {integrity: sha512-fFOXaluP1os/Tamx7gzwhT3tXPAfqZ8KYRC0UfXdXeUtlUIUfiGrIifDJ26/9uePmF8Zhqy2M0XjG8W9kQnJpg==}
peerDependencies: peerDependencies:
'@sveltejs/kit': 1.x || 2.x '@sveltejs/kit': 1.x || 2.x
svelte: 3.x || 4.x || >=5.0.0-next.51 svelte: 3.x || 4.x || >=5.0.0-next.51
peerDependenciesMeta:
'@sinclair/typebox':
optional: true
'@vinejs/vine':
optional: true
arktype:
optional: true
joi:
optional: true
superstruct:
optional: true
valibot:
optional: true
yup:
optional: true
zod:
optional: true
dependencies: dependencies:
'@sveltejs/kit': 2.5.4(@sveltejs/vite-plugin-svelte@3.0.2)(svelte@4.2.12)(vite@5.2.6) '@sveltejs/kit': 2.5.5(@sveltejs/vite-plugin-svelte@3.0.2)(svelte@4.2.12)(vite@5.2.7)
devalue: 4.3.2 devalue: 4.3.2
just-clone: 6.2.0 just-clone: 6.2.0
memoize-weak: 1.0.2 memoize-weak: 1.0.2
svelte: 4.2.12 svelte: 4.2.12
ts-deepmerge: 7.0.0 ts-deepmerge: 7.0.0
optionalDependencies: optionalDependencies:
'@gcornut/valibot-json-schema': 0.0.26(@types/json-schema@7.0.15)(esbuild-runner@2.2.2)(esbuild@0.20.2)(valibot@0.30.0) '@gcornut/valibot-json-schema': 0.0.27(@types/json-schema@7.0.15)(esbuild-runner@2.2.2)(esbuild@0.20.2)(valibot@0.30.0)
'@sinclair/typebox': 0.32.20 '@sinclair/typebox': 0.32.20
'@sodaru/yup-to-json-schema': 2.0.1 '@sodaru/yup-to-json-schema': 2.0.1
'@vinejs/vine': 1.8.0 '@vinejs/vine': 1.8.0
@ -7419,26 +7398,26 @@ packages:
'@babel/runtime': 7.24.1 '@babel/runtime': 7.24.1
dev: false dev: false
/tailwind-variants@0.2.1(tailwindcss@3.4.1): /tailwind-variants@0.2.1(tailwindcss@3.4.3):
resolution: {integrity: sha512-2xmhAf4UIc3PijOUcJPA1LP4AbxhpcHuHM2C26xM0k81r0maAO6uoUSHl3APmvHZcY5cZCY/bYuJdfFa4eGoaw==} resolution: {integrity: sha512-2xmhAf4UIc3PijOUcJPA1LP4AbxhpcHuHM2C26xM0k81r0maAO6uoUSHl3APmvHZcY5cZCY/bYuJdfFa4eGoaw==}
engines: {node: '>=16.x', pnpm: '>=7.x'} engines: {node: '>=16.x', pnpm: '>=7.x'}
peerDependencies: peerDependencies:
tailwindcss: '*' tailwindcss: '*'
dependencies: dependencies:
tailwind-merge: 2.2.2 tailwind-merge: 2.2.2
tailwindcss: 3.4.1(ts-node@10.9.2) tailwindcss: 3.4.3(ts-node@10.9.2)
dev: false dev: false
/tailwindcss-animate@1.0.7(tailwindcss@3.4.1): /tailwindcss-animate@1.0.7(tailwindcss@3.4.3):
resolution: {integrity: sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA==} resolution: {integrity: sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA==}
peerDependencies: peerDependencies:
tailwindcss: '>=3.0.0 || insiders' tailwindcss: '>=3.0.0 || insiders'
dependencies: dependencies:
tailwindcss: 3.4.1(ts-node@10.9.2) tailwindcss: 3.4.3(ts-node@10.9.2)
dev: false dev: false
/tailwindcss@3.4.1(ts-node@10.9.2): /tailwindcss@3.4.3(ts-node@10.9.2):
resolution: {integrity: sha512-qAYmXRfk3ENzuPBakNK0SRrUDipP8NQnEY6772uDhflcQz5EhRdD7JNZxyrFHVQNCwULPBn6FNPp9brpO7ctcA==} resolution: {integrity: sha512-U7sxQk/n397Bmx4JHbJx/iSOOv5G+II3f1kpLpY2QeUv5DcPdcTsYLlusZfq1NthHS1c1cZoyFmmkex1rzke0A==}
engines: {node: '>=14.0.0'} engines: {node: '>=14.0.0'}
hasBin: true hasBin: true
dependencies: dependencies:
@ -7578,7 +7557,7 @@ packages:
/ts-interface-checker@0.1.13: /ts-interface-checker@0.1.13:
resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==}
/ts-node@10.9.2(@types/node@20.11.30)(typescript@5.4.3): /ts-node@10.9.2(@types/node@20.12.2)(typescript@5.4.3):
resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==} resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==}
hasBin: true hasBin: true
peerDependencies: peerDependencies:
@ -7597,7 +7576,7 @@ packages:
'@tsconfig/node12': 1.0.11 '@tsconfig/node12': 1.0.11
'@tsconfig/node14': 1.0.3 '@tsconfig/node14': 1.0.3
'@tsconfig/node16': 1.0.4 '@tsconfig/node16': 1.0.4
'@types/node': 20.11.30 '@types/node': 20.12.2
acorn: 8.11.2 acorn: 8.11.2
acorn-walk: 8.3.0 acorn-walk: 8.3.0
arg: 4.1.3 arg: 4.1.3
@ -7746,7 +7725,7 @@ packages:
- rollup - rollup
dev: true dev: true
/vite-node@1.4.0(@types/node@20.11.30)(sass@1.72.0): /vite-node@1.4.0(@types/node@20.12.2)(sass@1.72.0):
resolution: {integrity: sha512-VZDAseqjrHgNd4Kh8icYHWzTKSCZMhia7GyHfhtzLW33fZlG9SwsB6CEhgyVOWkJfJ2pFLrp/Gj1FSfAiqH9Lw==} resolution: {integrity: sha512-VZDAseqjrHgNd4Kh8icYHWzTKSCZMhia7GyHfhtzLW33fZlG9SwsB6CEhgyVOWkJfJ2pFLrp/Gj1FSfAiqH9Lw==}
engines: {node: ^18.0.0 || >=20.0.0} engines: {node: ^18.0.0 || >=20.0.0}
hasBin: true hasBin: true
@ -7755,7 +7734,7 @@ packages:
debug: 4.3.4 debug: 4.3.4
pathe: 1.1.2 pathe: 1.1.2
picocolors: 1.0.0 picocolors: 1.0.0
vite: 5.2.6(@types/node@20.11.30)(sass@1.72.0) vite: 5.2.7(@types/node@20.12.2)(sass@1.72.0)
transitivePeerDependencies: transitivePeerDependencies:
- '@types/node' - '@types/node'
- less - less
@ -7767,8 +7746,8 @@ packages:
- terser - terser
dev: true dev: true
/vite@5.2.6(@types/node@20.11.30)(sass@1.72.0): /vite@5.2.7(@types/node@20.12.2)(sass@1.72.0):
resolution: {integrity: sha512-FPtnxFlSIKYjZ2eosBQamz4CbyrTizbZ3hnGJlh/wMtCrlp1Hah6AzBLjGI5I2urTfNnpovpHdrL6YRuBOPnCA==} resolution: {integrity: sha512-k14PWOKLI6pMaSzAuGtT+Cf0YmIx12z9YGon39onaJNy8DLBfBJrzg9FQEmkAM5lpHBZs9wksWAsyF/HkpEwJA==}
engines: {node: ^18.0.0 || >=20.0.0} engines: {node: ^18.0.0 || >=20.0.0}
hasBin: true hasBin: true
peerDependencies: peerDependencies:
@ -7795,7 +7774,7 @@ packages:
terser: terser:
optional: true optional: true
dependencies: dependencies:
'@types/node': 20.11.30 '@types/node': 20.12.2
esbuild: 0.20.2 esbuild: 0.20.2
postcss: 8.4.38 postcss: 8.4.38
rollup: 4.13.0 rollup: 4.13.0
@ -7803,7 +7782,7 @@ packages:
optionalDependencies: optionalDependencies:
fsevents: 2.3.3 fsevents: 2.3.3
/vitefu@0.2.5(vite@5.2.6): /vitefu@0.2.5(vite@5.2.7):
resolution: {integrity: sha512-SgHtMLoqaeeGnd2evZ849ZbACbnwQCIwRH57t18FxcXoZop0uQu0uzlIhJBlF/eWVzuce0sHeqPcDo+evVcg8Q==} resolution: {integrity: sha512-SgHtMLoqaeeGnd2evZ849ZbACbnwQCIwRH57t18FxcXoZop0uQu0uzlIhJBlF/eWVzuce0sHeqPcDo+evVcg8Q==}
peerDependencies: peerDependencies:
vite: ^3.0.0 || ^4.0.0 || ^5.0.0 vite: ^3.0.0 || ^4.0.0 || ^5.0.0
@ -7811,9 +7790,9 @@ packages:
vite: vite:
optional: true optional: true
dependencies: dependencies:
vite: 5.2.6(@types/node@20.11.30)(sass@1.72.0) vite: 5.2.7(@types/node@20.12.2)(sass@1.72.0)
/vitest@1.4.0(@types/node@20.11.30)(sass@1.72.0): /vitest@1.4.0(@types/node@20.12.2)(sass@1.72.0):
resolution: {integrity: sha512-gujzn0g7fmwf83/WzrDTnncZt2UiXP41mHuFYFrdwaLRVQ6JYQEiME2IfEjU3vcFL3VKa75XhI3lFgn+hfVsQw==} resolution: {integrity: sha512-gujzn0g7fmwf83/WzrDTnncZt2UiXP41mHuFYFrdwaLRVQ6JYQEiME2IfEjU3vcFL3VKa75XhI3lFgn+hfVsQw==}
engines: {node: ^18.0.0 || >=20.0.0} engines: {node: ^18.0.0 || >=20.0.0}
hasBin: true hasBin: true
@ -7838,7 +7817,7 @@ packages:
jsdom: jsdom:
optional: true optional: true
dependencies: dependencies:
'@types/node': 20.11.30 '@types/node': 20.12.2
'@vitest/expect': 1.4.0 '@vitest/expect': 1.4.0
'@vitest/runner': 1.4.0 '@vitest/runner': 1.4.0
'@vitest/snapshot': 1.4.0 '@vitest/snapshot': 1.4.0
@ -7856,8 +7835,8 @@ packages:
strip-literal: 2.0.0 strip-literal: 2.0.0
tinybench: 2.6.0 tinybench: 2.6.0
tinypool: 0.8.2 tinypool: 0.8.2
vite: 5.2.6(@types/node@20.11.30)(sass@1.72.0) vite: 5.2.7(@types/node@20.12.2)(sass@1.72.0)
vite-node: 1.4.0(@types/node@20.11.30)(sass@1.72.0) vite-node: 1.4.0(@types/node@20.12.2)(sass@1.72.0)
why-is-node-running: 2.2.2 why-is-node-running: 2.2.2
transitivePeerDependencies: transitivePeerDependencies:
- less - less

2
src/app.d.ts vendored
View file

@ -6,7 +6,7 @@
declare global { declare global {
namespace App { namespace App {
interface PageData { interface PageData {
flash?: { type: 'success' | 'error' | 'info'; message: string }; flash?: { type: 'success' | 'error' | 'info'; message: string; data: any };
} }
interface Locals { interface Locals {
auth: import('lucia').AuthRequest; auth: import('lucia').AuthRequest;

View file

@ -17,38 +17,37 @@ export const load: PageServerLoad = async (event) => {
const { user } = event.locals; const { user } = event.locals;
const dbUser = await db.query const dbUser = await db.query.users.findFirst({
.users where: eq(users.id, user.id),
.findFirst({ });
where: eq(users.id, user.id)
});
const profileForm = await superValidate(zod(profileSchema), { const profileForm = await superValidate(zod(profileSchema), {
defaults: { defaults: {
firstName: dbUser?.first_name ?? '', firstName: dbUser?.first_name ?? '',
lastName: dbUser?.last_name ?? '', lastName: dbUser?.last_name ?? '',
username: dbUser?.username ?? '', username: dbUser?.username ?? '',
} },
}); });
const emailForm = await superValidate(zod(changeEmailSchema), { const emailForm = await superValidate(zod(changeEmailSchema), {
defaults: { defaults: {
email: dbUser?.email ?? '', email: dbUser?.email ?? '',
} },
}); });
return { return {
profileForm, profileForm,
emailForm, emailForm,
hasSetupTwoFactor: !!dbUser?.two_factor_secret hasSetupTwoFactor: !!dbUser?.two_factor_secret,
}; };
}; };
const changeEmailIfNotEmpty = z.object({ const changeEmailIfNotEmpty = z.object({
email: z.string() email: z
.string()
.trim() .trim()
.max(64, { message: 'Email must be less than 64 characters' }) .max(64, { message: 'Email must be less than 64 characters' })
.email({ message: 'Please enter a valid email' }) .email({ message: 'Please enter a valid email' }),
}); });
export const actions: Actions = { export const actions: Actions = {
profileUpdate: async (event) => { profileUpdate: async (event) => {
@ -56,7 +55,7 @@ export const actions: Actions = {
if (!form.valid) { if (!form.valid) {
return fail(400, { return fail(400, {
form form,
}); });
} }
if (!event.locals.user) { if (!event.locals.user) {
@ -68,12 +67,9 @@ export const actions: Actions = {
const user = event.locals.user; const user = event.locals.user;
const newUsername = form.data.username; const newUsername = form.data.username;
const existingUser = await db.query const existingUser = await db.query.users.findFirst({
.users where: eq(users.username, newUsername),
.findFirst({ });
where: eq(users.username, newUsername)
}
);
if (existingUser && existingUser.id !== user.id) { if (existingUser && existingUser.id !== user.id) {
return setError(form, 'username', 'That username is already taken'); return setError(form, 'username', 'That username is already taken');
@ -84,7 +80,7 @@ export const actions: Actions = {
.set({ .set({
first_name: form.data.firstName, first_name: form.data.firstName,
last_name: form.data.lastName, last_name: form.data.lastName,
username: form.data.username username: form.data.username,
}) })
.where(eq(users.id, user.id)); .where(eq(users.id, user.id));
} catch (e) { } catch (e) {
@ -102,29 +98,30 @@ export const actions: Actions = {
const form = await superValidate(event, zod(changeEmailSchema)); const form = await superValidate(event, zod(changeEmailSchema));
const newEmail = form.data?.email; const newEmail = form.data?.email;
if (!form.valid || !newEmail || (newEmail !== '' && !changeEmailIfNotEmpty.safeParse(form.data).success)) { if (
!form.valid ||
!newEmail ||
(newEmail !== '' && !changeEmailIfNotEmpty.safeParse(form.data).success)
) {
return fail(400, { return fail(400, {
form form,
}); });
} }
if (!event.locals.user) { if (!event.locals.user) {
redirect(302, '/login', notSignedInMessage, event); redirect(302, '/login', notSignedInMessage, event);
} }
const user = event.locals.user; const user = event.locals.user;
const existingUser = await db.query.users.findFirst({ const existingUser = await db.query.users.findFirst({
where: eq(users.email, newEmail) where: eq(users.email, newEmail),
}); });
if (existingUser && existingUser.id !== user.id) { if (existingUser && existingUser.id !== user.id) {
return setError(form, 'email', 'That email is already taken'); return setError(form, 'email', 'That email is already taken');
} }
await db await db.update(users).set({ email: form.data.email }).where(eq(users.id, user.id));
.update(users)
.set({ email: form.data.email })
.where(eq(users.id, user.id));
if (user.email !== form.data.email) { if (user.email !== form.data.email) {
// Send email to confirm new email? // Send email to confirm new email?
@ -143,5 +140,5 @@ export const actions: Actions = {
} }
return message(form, { type: 'success', message: 'Email updated successfully!' }); return message(form, { type: 'success', message: 'Email updated successfully!' });
} },
}; };

View file

@ -1,19 +1,19 @@
import { encodeHex } from 'oslo/encoding'; import { type Actions, fail } from '@sveltejs/kit';
import { eq } from 'drizzle-orm';
import { createId as cuid2 } from '@paralleldrive/cuid2';
import { encodeHex, decodeHex } from 'oslo/encoding';
import { Argon2id } from 'oslo/password'; import { Argon2id } from 'oslo/password';
import { createTOTPKeyURI } from 'oslo/otp'; import { createTOTPKeyURI, TOTPController } from 'oslo/otp';
import { HMAC } from 'oslo/crypto'; import { HMAC } from 'oslo/crypto';
import QRCode from 'qrcode';
import { zod } from 'sveltekit-superforms/adapters'; import { zod } from 'sveltekit-superforms/adapters';
import { setError, superValidate } from 'sveltekit-superforms/server'; import { message, setError, superValidate } from 'sveltekit-superforms/server';
import { redirect } from 'sveltekit-flash-message/server'; import { redirect } from 'sveltekit-flash-message/server';
import type { Cookie } from 'lucia';
import type { PageServerLoad } from '../../$types'; import type { PageServerLoad } from '../../$types';
import { addTwoFactorSchema } from '$lib/validations/account'; import { addTwoFactorSchema } from '$lib/validations/account';
import { notSignedInMessage } from '$lib/flashMessages'; import { notSignedInMessage } from '$lib/flashMessages';
import { type Actions, fail } from '@sveltejs/kit';
import db from '$lib/drizzle'; import db from '$lib/drizzle';
import { eq } from 'drizzle-orm'; import { recovery_codes, users } from '../../../../../../schema';
import { users } from '../../../../../../schema';
import QRCode from 'qrcode';
export const load: PageServerLoad = async (event) => { export const load: PageServerLoad = async (event) => {
const form = await superValidate(event, zod(addTwoFactorSchema)); const form = await superValidate(event, zod(addTwoFactorSchema));
@ -23,25 +23,42 @@ export const load: PageServerLoad = async (event) => {
redirect(302, '/login', notSignedInMessage, event); redirect(302, '/login', notSignedInMessage, event);
} }
const dbUser = await db.query.users.findFirst({
where: eq(users.id, user.id),
});
if (dbUser?.two_factor_enabled) {
const message = {
type: 'info',
message: 'Two-Factor Authentication is already enabled',
} as const;
throw redirect('/profile', message, event);
}
const twoFactorSecret = await new HMAC('SHA-1').generateKey(); const twoFactorSecret = await new HMAC('SHA-1').generateKey();
await db await db
.update(users) .update(users)
.set({ two_factor_secret: encodeHex(twoFactorSecret) }) .set({
two_factor_secret: encodeHex(twoFactorSecret),
two_factor_enabled: false,
})
.where(eq(users.id, user.id)); .where(eq(users.id, user.id));
const issuer = 'bored-game'; const issuer = 'bored-game';
const accountName = user.email || user.username; const accountName = user.email || user.username;
// pass the website's name and the user identifier (e.g. email, username) // pass the website's name and the user identifier (e.g. email, username)
const uri = createTOTPKeyURI(issuer, accountName, twoFactorSecret); const totpUri = createTOTPKeyURI(issuer, accountName, twoFactorSecret);
const qrCode = await QRCode.toDataURL(uri); const qrCode = QRCode.toDataURL(totpUri);
form.data = { form.data = {
current_password: '', current_password: '',
two_factor_code: '' two_factor_code: '',
}; };
return { return {
form, form,
qrCode twoFactorEnabled: false,
totpUri,
qrCode,
}; };
}; };
@ -51,11 +68,10 @@ export const actions: Actions = {
if (!form.valid) { if (!form.valid) {
return fail(400, { return fail(400, {
form form,
}); });
} }
console.log('updating profile');
if (!event.locals.user) { if (!event.locals.user) {
redirect(302, '/login', notSignedInMessage, event); redirect(302, '/login', notSignedInMessage, event);
} }
@ -67,7 +83,7 @@ export const actions: Actions = {
const user = event.locals.user; const user = event.locals.user;
const dbUser = await db.query.users.findFirst({ const dbUser = await db.query.users.findFirst({
where: eq(users.id, user.id) where: eq(users.id, user.id),
}); });
if (!dbUser?.hashed_password) { if (!dbUser?.hashed_password) {
@ -75,47 +91,60 @@ export const actions: Actions = {
form.data.two_factor_code = ''; form.data.two_factor_code = '';
return setError( return setError(
form, form,
'Error occurred. Please try again or contact support if you need further help.' 'Error occurred. Please try again or contact support if you need further help.',
);
}
if (dbUser?.two_factor_secret === '' || dbUser?.two_factor_secret === null) {
form.data.current_password = '';
form.data.two_factor_code = '';
return setError(
form,
'Error occurred. Please try again or contact support if you need further help.',
); );
} }
const currentPasswordVerified = await new Argon2id().verify( const currentPasswordVerified = await new Argon2id().verify(
dbUser.hashed_password, dbUser.hashed_password,
form.data.current_password form.data.current_password,
); );
if (!currentPasswordVerified) { if (!currentPasswordVerified) {
return setError(form, 'current_password', 'Your password is incorrect'); return setError(form, 'current_password', 'Your password is incorrect');
} }
if (user?.username) {
let sessionCookie: Cookie;
try {
} catch (e) {
console.error(e);
form.data.password = '';
form.data.confirm_password = '';
form.data.current_password = '';
return setError(form, 'current_password', 'Your password is incorrect.');
}
event.cookies.set(sessionCookie.name, sessionCookie.value, {
path: '.',
...sessionCookie.attributes
});
const message = { if (form.data.two_factor_code === '') {
type: 'success', return setError(form, 'two_factor_code', 'Please enter a code');
message: 'Password Updated. Please sign in.'
} as const;
redirect(302, '/login', message, event);
} }
return setError(
form, const twoFactorCode = form.data.two_factor_code;
'Error occurred. Please try again or contact support if you need further help.' const validOTP = await new TOTPController().verify(
twoFactorCode,
decodeHex(dbUser.two_factor_secret),
); );
// TODO: Add toast instead?
// form.data.password = ''; if (!validOTP) {
// form.data.confirm_password = ''; return setError(form, 'two_factor_code', 'Invalid code');
// form.data.current_password = ''; }
// return message(form, 'Profile updated successfully.');
} await db.update(users).set({ two_factor_enabled: true }).where(eq(users.id, user.id));
const recoveryCodes = generateRecoveryCodes();
if (recoveryCodes) {
for (const code of recoveryCodes) {
await db.insert(recovery_codes).values({
userId: user.id,
code: await new Argon2id().hash(code),
});
}
}
form.data.current_password = '';
form.data.two_factor_code = '';
return { recoveryCodes };
},
}; };
function generateRecoveryCodes() {
return Array.from({ length: 5 }, () => cuid2());
}

View file

@ -2,28 +2,28 @@
import { zodClient } from 'sveltekit-superforms/adapters'; import { zodClient } from 'sveltekit-superforms/adapters';
import { superForm } from 'sveltekit-superforms/client'; import { superForm } from 'sveltekit-superforms/client';
import { AlertTriangle } from 'lucide-svelte'; import { AlertTriangle } from 'lucide-svelte';
import * as Alert from "$components/ui/alert"; import * as Alert from '$components/ui/alert';
import * as Form from '$components/ui/form'; import * as Form from '$components/ui/form';
import { Input } from '$components/ui/input'; import { Input } from '$components/ui/input';
import { addTwoFactorSchema } from '$lib/validations/account'; import { addTwoFactorSchema } from '$lib/validations/account';
export let data; export let data;
export let form;
console.log('recovery codes', form.recoveryCodes)
const { qrCode } = data; const { qrCode } = data;
console.log('qrCode', qrCode) const twoFactorForm = superForm(data.form, {
const form = superForm(data.form, {
taintedMessage: null, taintedMessage: null,
validators: zodClient(addTwoFactorSchema), validators: zodClient(addTwoFactorSchema),
delayMs: 500, delayMs: 500,
multipleSubmits: 'prevent' multipleSubmits: 'prevent',
}); });
const { form: formData, enhance } = form; const { form: formData, enhance } = twoFactorForm;
</script> </script>
<h1>Two-Factor Authentication</h1> <h1>Two-Factor Authentication</h1>
<img src={qrCode} alt="QR Code" /> <img src={qrCode} alt="QR Code" />

View file

@ -91,7 +91,8 @@ export const actions: Actions = {
verified: false, verified: false,
receive_email: false, receive_email: false,
theme: 'system', theme: 'system',
two_factor_secret: null two_factor_secret: '',
two_factor_enabled: false
}) })
.returning(); .returning();
console.log('signup user', user); console.log('signup user', user);

View file

@ -8,7 +8,7 @@ import {
index, index,
pgEnum, pgEnum,
primaryKey, primaryKey,
uuid uuid,
} from 'drizzle-orm/pg-core'; } from 'drizzle-orm/pg-core';
import { createId as cuid2 } from '@paralleldrive/cuid2'; import { createId as cuid2 } from '@paralleldrive/cuid2';
import { tsvector } from './tsVector'; import { tsvector } from './tsVector';
@ -28,13 +28,25 @@ export const users = pgTable('users', {
verified: boolean('verified').default(false), verified: boolean('verified').default(false),
receive_email: boolean('receive_email').default(false), receive_email: boolean('receive_email').default(false),
theme: text('theme').default('system'), theme: text('theme').default('system'),
two_factor_secret: text('two_factor_secret'), two_factor_secret: text('two_factor_secret').default(''),
two_factor_enabled: boolean('two_factor_enabled').default(false),
created_at: timestamp('created_at').notNull().defaultNow(), created_at: timestamp('created_at').notNull().defaultNow(),
updated_at: timestamp('updated_at').notNull().defaultNow() updated_at: timestamp('updated_at').notNull().defaultNow(),
});
export const recovery_codes = pgTable('recovery_codes', {
id: uuid('id').primaryKey().defaultRandom(),
userId: uuid('user_id')
.notNull()
.references(() => users.id),
code: text('code').notNull(),
used: boolean('used').default(false),
created_at: timestamp('created_at').notNull().defaultNow(),
updated_at: timestamp('updated_at').notNull().defaultNow(),
}); });
export const user_relations = relations(users, ({ many }) => ({ export const user_relations = relations(users, ({ many }) => ({
user_roles: many(user_roles) user_roles: many(user_roles),
})); }));
export type Users = InferSelectModel<typeof users>; export type Users = InferSelectModel<typeof users>;
@ -46,10 +58,10 @@ export const sessions = pgTable('sessions', {
.references(() => users.id), .references(() => users.id),
expiresAt: timestamp('expires_at', { expiresAt: timestamp('expires_at', {
withTimezone: true, withTimezone: true,
mode: 'date' mode: 'date',
}).notNull(), }).notNull(),
ipCountry: text('ip_country'), ipCountry: text('ip_country'),
ipAddress: text('ip_address') ipAddress: text('ip_address'),
}); });
export const roles = pgTable('roles', { export const roles = pgTable('roles', {
@ -58,13 +70,13 @@ export const roles = pgTable('roles', {
.unique() .unique()
.$defaultFn(() => cuid2()) .$defaultFn(() => cuid2())
.notNull(), .notNull(),
name: text('name').unique().notNull() name: text('name').unique().notNull(),
}); });
export type Roles = InferSelectModel<typeof roles>; export type Roles = InferSelectModel<typeof roles>;
export const role_relations = relations(roles, ({ many }) => ({ export const role_relations = relations(roles, ({ many }) => ({
user_roles: many(user_roles) user_roles: many(user_roles),
})); }));
export const user_roles = pgTable('user_roles', { export const user_roles = pgTable('user_roles', {
@ -80,18 +92,18 @@ export const user_roles = pgTable('user_roles', {
.references(() => roles.id, { onDelete: 'cascade' }), .references(() => roles.id, { onDelete: 'cascade' }),
primary: boolean('primary').default(false), primary: boolean('primary').default(false),
created_at: timestamp('created_at').notNull().defaultNow(), created_at: timestamp('created_at').notNull().defaultNow(),
updated_at: timestamp('updated_at').notNull().defaultNow() updated_at: timestamp('updated_at').notNull().defaultNow(),
}); });
export const user_role_relations = relations(user_roles, ({ one }) => ({ export const user_role_relations = relations(user_roles, ({ one }) => ({
role: one(roles, { role: one(roles, {
fields: [user_roles.role_id], fields: [user_roles.role_id],
references: [roles.id] references: [roles.id],
}), }),
user: one(users, { user: one(users, {
fields: [user_roles.user_id], fields: [user_roles.user_id],
references: [users.id] references: [users.id],
}) }),
})); }));
export type UserRoles = InferSelectModel<typeof user_roles>; export type UserRoles = InferSelectModel<typeof user_roles>;
@ -106,16 +118,16 @@ export const password_reset_tokens = pgTable('password_reset_tokens', {
expires_at: timestamp('expires_at', { expires_at: timestamp('expires_at', {
withTimezone: true, withTimezone: true,
mode: 'date', mode: 'date',
precision: 6 precision: 6,
}), }),
created_at: timestamp('created_at').notNull().defaultNow() created_at: timestamp('created_at').notNull().defaultNow(),
}); });
export const password_reset_token_relations = relations(password_reset_tokens, ({ one }) => ({ export const password_reset_token_relations = relations(password_reset_tokens, ({ one }) => ({
user: one(users, { user: one(users, {
fields: [password_reset_tokens.user_id], fields: [password_reset_tokens.user_id],
references: [users.id] references: [users.id],
}) }),
})); }));
export const collections = pgTable('collections', { export const collections = pgTable('collections', {
@ -127,14 +139,14 @@ export const collections = pgTable('collections', {
.notNull() .notNull()
.references(() => users.id, { onDelete: 'cascade' }), .references(() => users.id, { onDelete: 'cascade' }),
created_at: timestamp('created_at').notNull().defaultNow(), created_at: timestamp('created_at').notNull().defaultNow(),
updated_at: timestamp('updated_at').notNull().defaultNow() updated_at: timestamp('updated_at').notNull().defaultNow(),
}); });
export const collection_relations = relations(collections, ({ one }) => ({ export const collection_relations = relations(collections, ({ one }) => ({
user: one(users, { user: one(users, {
fields: [collections.user_id], fields: [collections.user_id],
references: [users.id] references: [users.id],
}) }),
})); }));
export const collection_items = pgTable('collection_items', { export const collection_items = pgTable('collection_items', {
@ -150,7 +162,7 @@ export const collection_items = pgTable('collection_items', {
.references(() => games.id, { onDelete: 'cascade' }), .references(() => games.id, { onDelete: 'cascade' }),
times_played: integer('times_played').default(0), times_played: integer('times_played').default(0),
created_at: timestamp('created_at').notNull().defaultNow(), created_at: timestamp('created_at').notNull().defaultNow(),
updated_at: timestamp('updated_at').notNull().defaultNow() updated_at: timestamp('updated_at').notNull().defaultNow(),
}); });
export type CollectionItems = InferSelectModel<typeof collection_items>; export type CollectionItems = InferSelectModel<typeof collection_items>;
@ -158,12 +170,12 @@ export type CollectionItems = InferSelectModel<typeof collection_items>;
export const collection_item_relations = relations(collection_items, ({ one }) => ({ export const collection_item_relations = relations(collection_items, ({ one }) => ({
collection: one(collections, { collection: one(collections, {
fields: [collection_items.collection_id], fields: [collection_items.collection_id],
references: [collections.id] references: [collections.id],
}), }),
game: one(games, { game: one(games, {
fields: [collection_items.game_id], fields: [collection_items.game_id],
references: [games.id] references: [games.id],
}) }),
})); }));
export const wishlists = pgTable('wishlists', { export const wishlists = pgTable('wishlists', {
@ -175,7 +187,7 @@ export const wishlists = pgTable('wishlists', {
.notNull() .notNull()
.references(() => users.id, { onDelete: 'cascade' }), .references(() => users.id, { onDelete: 'cascade' }),
created_at: timestamp('created_at').notNull().defaultNow(), created_at: timestamp('created_at').notNull().defaultNow(),
updated_at: timestamp('updated_at').notNull().defaultNow() updated_at: timestamp('updated_at').notNull().defaultNow(),
}); });
export type Wishlists = InferSelectModel<typeof wishlists>; export type Wishlists = InferSelectModel<typeof wishlists>;
@ -183,8 +195,8 @@ export type Wishlists = InferSelectModel<typeof wishlists>;
export const wishlists_relations = relations(wishlists, ({ one }) => ({ export const wishlists_relations = relations(wishlists, ({ one }) => ({
user: one(users, { user: one(users, {
fields: [wishlists.user_id], fields: [wishlists.user_id],
references: [users.id] references: [users.id],
}) }),
})); }));
export const wishlist_items = pgTable('wishlist_items', { export const wishlist_items = pgTable('wishlist_items', {
@ -199,7 +211,7 @@ export const wishlist_items = pgTable('wishlist_items', {
.notNull() .notNull()
.references(() => games.id, { onDelete: 'cascade' }), .references(() => games.id, { onDelete: 'cascade' }),
created_at: timestamp('created_at').notNull().defaultNow(), created_at: timestamp('created_at').notNull().defaultNow(),
updated_at: timestamp('updated_at').notNull().defaultNow() updated_at: timestamp('updated_at').notNull().defaultNow(),
}); });
export type WishlistItems = InferSelectModel<typeof wishlist_items>; export type WishlistItems = InferSelectModel<typeof wishlist_items>;
@ -207,12 +219,12 @@ export type WishlistItems = InferSelectModel<typeof wishlist_items>;
export const wishlist_item_relations = relations(wishlist_items, ({ one }) => ({ export const wishlist_item_relations = relations(wishlist_items, ({ one }) => ({
wishlist: one(wishlists, { wishlist: one(wishlists, {
fields: [wishlist_items.wishlist_id], fields: [wishlist_items.wishlist_id],
references: [wishlists.id] references: [wishlists.id],
}), }),
game: one(games, { game: one(games, {
fields: [wishlist_items.game_id], fields: [wishlist_items.game_id],
references: [games.id] references: [games.id],
}) }),
})); }));
// Game and related table schemas // Game and related table schemas
@ -223,7 +235,7 @@ export const externalIdType = pgEnum('external_id_type', [
'mechanic', 'mechanic',
'publisher', 'publisher',
'designer', 'designer',
'artist' 'artist',
]); ]);
export const externalIds = pgTable('external_ids', { export const externalIds = pgTable('external_ids', {
@ -232,7 +244,7 @@ export const externalIds = pgTable('external_ids', {
.unique() .unique()
.$defaultFn(() => cuid2()), .$defaultFn(() => cuid2()),
type: externalIdType('type').notNull(), type: externalIdType('type').notNull(),
externalId: text('external_id').notNull() externalId: text('external_id').notNull(),
}); });
export type ExternalIds = InferSelectModel<typeof externalIds>; export type ExternalIds = InferSelectModel<typeof externalIds>;
@ -261,18 +273,18 @@ export const games = pgTable(
last_sync_at: timestamp('last_sync_at', { last_sync_at: timestamp('last_sync_at', {
withTimezone: true, withTimezone: true,
mode: 'date', mode: 'date',
precision: 6 precision: 6,
}), }),
created_at: timestamp('created_at').notNull().defaultNow(), created_at: timestamp('created_at').notNull().defaultNow(),
updated_at: timestamp('updated_at').notNull().defaultNow() updated_at: timestamp('updated_at').notNull().defaultNow(),
}, },
(table) => { (table) => {
return { return {
text_searchable_idx: index('text_searchable_idx') text_searchable_idx: index('text_searchable_idx')
.on(table.text_searchable_index) .on(table.text_searchable_index)
.using(sql`'gin'`) .using(sql`'gin'`),
}; };
} },
); );
export type Games = InferSelectModel<typeof games>; export type Games = InferSelectModel<typeof games>;
@ -285,22 +297,22 @@ export const gamesToExternalIds = pgTable(
.references(() => games.id, { onDelete: 'restrict', onUpdate: 'cascade' }), .references(() => games.id, { onDelete: 'restrict', onUpdate: 'cascade' }),
externalId: uuid('external_id') externalId: uuid('external_id')
.notNull() .notNull()
.references(() => externalIds.id, { onDelete: 'restrict', onUpdate: 'cascade' }) .references(() => externalIds.id, { onDelete: 'restrict', onUpdate: 'cascade' }),
}, },
(table) => { (table) => {
return { return {
gamesToExternalIdsPkey: primaryKey({ gamesToExternalIdsPkey: primaryKey({
columns: [table.gameId, table.externalId] columns: [table.gameId, table.externalId],
}) }),
}; };
} },
); );
export const gameRelations = relations(games, ({ many }) => ({ export const gameRelations = relations(games, ({ many }) => ({
categories_to_games: many(categories_to_games), categories_to_games: many(categories_to_games),
mechanics_to_games: many(mechanics_to_games), mechanics_to_games: many(mechanics_to_games),
publishers_to_games: many(publishers_to_games), publishers_to_games: many(publishers_to_games),
gamesToExternalIds: many(gamesToExternalIds) gamesToExternalIds: many(gamesToExternalIds),
})); }));
export const expansions = pgTable('expansions', { export const expansions = pgTable('expansions', {
@ -315,7 +327,7 @@ export const expansions = pgTable('expansions', {
.notNull() .notNull()
.references(() => games.id, { onDelete: 'restrict', onUpdate: 'cascade' }), .references(() => games.id, { onDelete: 'restrict', onUpdate: 'cascade' }),
created_at: timestamp('created_at').notNull().defaultNow(), created_at: timestamp('created_at').notNull().defaultNow(),
updated_at: timestamp('updated_at').notNull().defaultNow() updated_at: timestamp('updated_at').notNull().defaultNow(),
}); });
export type Expansions = InferSelectModel<typeof expansions>; export type Expansions = InferSelectModel<typeof expansions>;
@ -323,12 +335,12 @@ export type Expansions = InferSelectModel<typeof expansions>;
export const expansion_relations = relations(expansions, ({ one }) => ({ export const expansion_relations = relations(expansions, ({ one }) => ({
baseGame: one(games, { baseGame: one(games, {
fields: [expansions.base_game_id], fields: [expansions.base_game_id],
references: [games.id] references: [games.id],
}), }),
game: one(games, { game: one(games, {
fields: [expansions.game_id], fields: [expansions.game_id],
references: [games.id] references: [games.id],
}) }),
})); }));
export const publishers = pgTable('publishers', { export const publishers = pgTable('publishers', {
@ -339,7 +351,7 @@ export const publishers = pgTable('publishers', {
name: text('name'), name: text('name'),
slug: text('slug'), slug: text('slug'),
created_at: timestamp('created_at').notNull().defaultNow(), created_at: timestamp('created_at').notNull().defaultNow(),
updated_at: timestamp('updated_at').notNull().defaultNow() updated_at: timestamp('updated_at').notNull().defaultNow(),
}); });
export type Publishers = InferSelectModel<typeof publishers>; export type Publishers = InferSelectModel<typeof publishers>;
@ -352,20 +364,20 @@ export const publishersToExternalIds = pgTable(
.references(() => publishers.id, { onDelete: 'restrict', onUpdate: 'cascade' }), .references(() => publishers.id, { onDelete: 'restrict', onUpdate: 'cascade' }),
externalId: uuid('external_id') externalId: uuid('external_id')
.notNull() .notNull()
.references(() => externalIds.id, { onDelete: 'restrict', onUpdate: 'cascade' }) .references(() => externalIds.id, { onDelete: 'restrict', onUpdate: 'cascade' }),
}, },
(table) => { (table) => {
return { return {
publishersToExternalIdsPkey: primaryKey({ publishersToExternalIdsPkey: primaryKey({
columns: [table.publisherId, table.externalId] columns: [table.publisherId, table.externalId],
}) }),
}; };
} },
); );
export const publishers_relations = relations(publishers, ({ many }) => ({ export const publishers_relations = relations(publishers, ({ many }) => ({
publishers_to_games: many(publishers_to_games), publishers_to_games: many(publishers_to_games),
publishersToExternalIds: many(publishersToExternalIds) publishersToExternalIds: many(publishersToExternalIds),
})); }));
export const categories = pgTable('categories', { export const categories = pgTable('categories', {
@ -376,7 +388,7 @@ export const categories = pgTable('categories', {
name: text('name'), name: text('name'),
slug: text('slug'), slug: text('slug'),
created_at: timestamp('created_at').notNull().defaultNow(), created_at: timestamp('created_at').notNull().defaultNow(),
updated_at: timestamp('updated_at').notNull().defaultNow() updated_at: timestamp('updated_at').notNull().defaultNow(),
}); });
export type Categories = InferSelectModel<typeof categories>; export type Categories = InferSelectModel<typeof categories>;
@ -389,15 +401,15 @@ export const categoriesToExternalIds = pgTable(
.references(() => categories.id, { onDelete: 'restrict', onUpdate: 'cascade' }), .references(() => categories.id, { onDelete: 'restrict', onUpdate: 'cascade' }),
externalId: uuid('external_id') externalId: uuid('external_id')
.notNull() .notNull()
.references(() => externalIds.id, { onDelete: 'restrict', onUpdate: 'cascade' }) .references(() => externalIds.id, { onDelete: 'restrict', onUpdate: 'cascade' }),
}, },
(table) => { (table) => {
return { return {
categoriesToExternalIdsPkey: primaryKey({ categoriesToExternalIdsPkey: primaryKey({
columns: [table.categoryId, table.externalId] columns: [table.categoryId, table.externalId],
}) }),
}; };
} },
); );
export const categories_to_games = pgTable( export const categories_to_games = pgTable(
@ -408,31 +420,31 @@ export const categories_to_games = pgTable(
.references(() => categories.id, { onDelete: 'restrict', onUpdate: 'cascade' }), .references(() => categories.id, { onDelete: 'restrict', onUpdate: 'cascade' }),
game_id: uuid('game_id') game_id: uuid('game_id')
.notNull() .notNull()
.references(() => games.id, { onDelete: 'restrict', onUpdate: 'cascade' }) .references(() => games.id, { onDelete: 'restrict', onUpdate: 'cascade' }),
}, },
(table) => { (table) => {
return { return {
categoriesToGamesPkey: primaryKey({ categoriesToGamesPkey: primaryKey({
columns: [table.category_id, table.game_id] columns: [table.category_id, table.game_id],
}) }),
}; };
} },
); );
export const categories_to_games_relations = relations(categories_to_games, ({ one }) => ({ export const categories_to_games_relations = relations(categories_to_games, ({ one }) => ({
category: one(categories, { category: one(categories, {
fields: [categories_to_games.category_id], fields: [categories_to_games.category_id],
references: [categories.id] references: [categories.id],
}), }),
game: one(games, { game: one(games, {
fields: [categories_to_games.game_id], fields: [categories_to_games.game_id],
references: [games.id] references: [games.id],
}) }),
})); }));
export const categories_relations = relations(categories, ({ many }) => ({ export const categories_relations = relations(categories, ({ many }) => ({
categories_to_games: many(categories_to_games), categories_to_games: many(categories_to_games),
categoriesToExternalIds: many(categoriesToExternalIds) categoriesToExternalIds: many(categoriesToExternalIds),
})); }));
export const mechanics = pgTable('mechanics', { export const mechanics = pgTable('mechanics', {
@ -443,7 +455,7 @@ export const mechanics = pgTable('mechanics', {
name: text('name'), name: text('name'),
slug: text('slug'), slug: text('slug'),
created_at: timestamp('created_at').notNull().defaultNow(), created_at: timestamp('created_at').notNull().defaultNow(),
updated_at: timestamp('updated_at').notNull().defaultNow() updated_at: timestamp('updated_at').notNull().defaultNow(),
}); });
export type Mechanics = InferSelectModel<typeof mechanics>; export type Mechanics = InferSelectModel<typeof mechanics>;
@ -456,20 +468,20 @@ export const mechanicsToExternalIds = pgTable(
.references(() => mechanics.id, { onDelete: 'restrict', onUpdate: 'cascade' }), .references(() => mechanics.id, { onDelete: 'restrict', onUpdate: 'cascade' }),
externalId: uuid('external_id') externalId: uuid('external_id')
.notNull() .notNull()
.references(() => externalIds.id, { onDelete: 'restrict', onUpdate: 'cascade' }) .references(() => externalIds.id, { onDelete: 'restrict', onUpdate: 'cascade' }),
}, },
(table) => { (table) => {
return { return {
mechanicsToExternalIdsPkey: primaryKey({ mechanicsToExternalIdsPkey: primaryKey({
columns: [table.mechanicId, table.externalId] columns: [table.mechanicId, table.externalId],
}) }),
}; };
} },
); );
export const mechanic_relations = relations(mechanics, ({ many }) => ({ export const mechanic_relations = relations(mechanics, ({ many }) => ({
mechanics_to_games: many(mechanics_to_games), mechanics_to_games: many(mechanics_to_games),
mechanicsToExternalIds: many(mechanicsToExternalIds) mechanicsToExternalIds: many(mechanicsToExternalIds),
})); }));
export const mechanics_to_games = pgTable( export const mechanics_to_games = pgTable(
@ -480,26 +492,26 @@ export const mechanics_to_games = pgTable(
.references(() => mechanics.id, { onDelete: 'restrict', onUpdate: 'cascade' }), .references(() => mechanics.id, { onDelete: 'restrict', onUpdate: 'cascade' }),
game_id: uuid('game_id') game_id: uuid('game_id')
.notNull() .notNull()
.references(() => games.id, { onDelete: 'restrict', onUpdate: 'cascade' }) .references(() => games.id, { onDelete: 'restrict', onUpdate: 'cascade' }),
}, },
(table) => { (table) => {
return { return {
mechanicsToGamesPkey: primaryKey({ mechanicsToGamesPkey: primaryKey({
columns: [table.mechanic_id, table.game_id] columns: [table.mechanic_id, table.game_id],
}) }),
}; };
} },
); );
export const mechanics_to_games_relations = relations(mechanics_to_games, ({ one }) => ({ export const mechanics_to_games_relations = relations(mechanics_to_games, ({ one }) => ({
mechanic: one(mechanics, { mechanic: one(mechanics, {
fields: [mechanics_to_games.mechanic_id], fields: [mechanics_to_games.mechanic_id],
references: [mechanics.id] references: [mechanics.id],
}), }),
game: one(games, { game: one(games, {
fields: [mechanics_to_games.game_id], fields: [mechanics_to_games.game_id],
references: [games.id] references: [games.id],
}) }),
})); }));
export const publishers_to_games = pgTable( export const publishers_to_games = pgTable(
@ -510,24 +522,24 @@ export const publishers_to_games = pgTable(
.references(() => publishers.id, { onDelete: 'restrict', onUpdate: 'cascade' }), .references(() => publishers.id, { onDelete: 'restrict', onUpdate: 'cascade' }),
game_id: uuid('game_id') game_id: uuid('game_id')
.notNull() .notNull()
.references(() => games.id, { onDelete: 'restrict', onUpdate: 'cascade' }) .references(() => games.id, { onDelete: 'restrict', onUpdate: 'cascade' }),
}, },
(table) => { (table) => {
return { return {
publishersToGamesPkey: primaryKey({ publishersToGamesPkey: primaryKey({
columns: [table.publisher_id, table.game_id] columns: [table.publisher_id, table.game_id],
}) }),
}; };
} },
); );
export const publishers_to_games_relations = relations(publishers_to_games, ({ one }) => ({ export const publishers_to_games_relations = relations(publishers_to_games, ({ one }) => ({
publisher: one(publishers, { publisher: one(publishers, {
fields: [publishers_to_games.publisher_id], fields: [publishers_to_games.publisher_id],
references: [publishers.id] references: [publishers.id],
}), }),
game: one(games, { game: one(games, {
fields: [publishers_to_games.game_id], fields: [publishers_to_games.game_id],
references: [games.id] references: [games.id],
}) }),
})); }));

View file

@ -2,40 +2,71 @@ import 'dotenv/config';
import { drizzle } from 'drizzle-orm/node-postgres'; import { drizzle } from 'drizzle-orm/node-postgres';
import pg from 'pg'; import pg from 'pg';
import * as schema from './schema'; import * as schema from './schema';
import {Argon2id} from "oslo/password";
// create the connection // create the connection
const pool = new pg.Pool({ const pool = new pg.Pool({
user: process.env.DATABASE_USER, user: process.env.DATABASE_USER,
password: process.env.DATABASE_PASSWORD, password: process.env.DATABASE_PASSWORD,
host: process.env.DATABASE_HOST, host: process.env.DATABASE_HOST,
port: new Number(process.env.DATABASE_PORT).valueOf(), port: Number(process.env.DATABASE_PORT).valueOf(),
database: process.env.DATABASE_DB, database: process.env.DATABASE_DB,
ssl: process.env.DATABASE_HOST === 'localhost' ? false : true ssl: process.env.DATABASE_HOST !== 'localhost'
}); });
const db = drizzle(pool, { schema: schema }); const db = drizzle(pool, { schema: schema });
const existingRoles = await db.query.roles.findMany();
console.log('Existing roles', existingRoles);
console.log('Creating roles ...'); console.log('Creating roles ...');
await db const adminRole = await db
.insert(schema.roles) .insert(schema.roles)
.values([{ name: 'admin' }]) .values([{ name: 'admin' }])
.onConflictDoNothing(); .onConflictDoNothing()
await db .returning();
const userRole = await db
.insert(schema.roles) .insert(schema.roles)
.values([{ name: 'user' }]) .values([{ name: 'user' }])
.onConflictDoNothing(); .onConflictDoNothing()
.returning();
await db await db
.insert(schema.roles) .insert(schema.roles)
.values([{ name: 'editor' }]) .values([{ name: 'editor' }])
.onConflictDoNothing(); .onConflictDoNothing();
console.log('Roles created.');
await db await db
.insert(schema.roles) .insert(schema.roles)
.values([{ name: 'moderator' }]) .values([{ name: 'moderator' }])
.onConflictDoNothing(); .onConflictDoNothing();
console.log('Roles created.'); console.log('Roles created.');
console.log('Admin Role: ', adminRole);
const adminUser = await db
.insert(schema.users)
.values({
username: `${process.env.ADMIN_USERNAME}`,
email: '',
hashed_password: await new Argon2id().hash(`${process.env.ADMIN_PASSWORD}`),
first_name: 'Brad',
last_name: 'S',
verified: true
})
.returning()
.onConflictDoNothing();
console.log('Admin user created.', adminUser);
await db.insert(schema.user_roles).values({
user_id: adminUser[0].id,
role_id: adminRole[0].id
}).onConflictDoNothing();
console.log('Admin user given admin role.');
await db.insert(schema.user_roles).values({
user_id: adminUser[0].id,
role_id: userRole[0].id
}).onConflictDoNothing();
console.log('Admin user given user role.');
await pool.end(); await pool.end();
process.exit(); process.exit();