Starting over migrations, revert schema date string type, using two variables in session for knowing if 2FA is valid, adding checks everywhere on protected routes and APIs.

This commit is contained in:
Bradley Shellnut 2024-06-11 19:12:12 -07:00
parent 36aa0636a3
commit e4ff068fe1
52 changed files with 718 additions and 4900 deletions

View file

@ -10,7 +10,7 @@ export default defineConfig({
port: Number(process.env.DATABASE_PORT) || 5432, port: Number(process.env.DATABASE_PORT) || 5432,
user: process.env.DATABASE_USER, user: process.env.DATABASE_USER,
password: process.env.DATABASE_PASSWORD, password: process.env.DATABASE_PASSWORD,
database: process.env.DATABASE || 'boredgame', database: process.env.DATABASE_DB || 'boredgame',
ssl: process.env.DATABASE_HOST !== 'localhost', ssl: process.env.DATABASE_HOST !== 'localhost',
}, },
// Print all statements // Print all statements

View file

@ -36,10 +36,10 @@
"@typescript-eslint/parser": "^7.12.0", "@typescript-eslint/parser": "^7.12.0",
"autoprefixer": "^10.4.19", "autoprefixer": "^10.4.19",
"dotenv": "^16.4.5", "dotenv": "^16.4.5",
"drizzle-kit": "^0.22.6", "drizzle-kit": "^0.22.7",
"eslint": "^8.57.0", "eslint": "^8.57.0",
"eslint-config-prettier": "^9.1.0", "eslint-config-prettier": "^9.1.0",
"eslint-plugin-svelte": "^2.39.0", "eslint-plugin-svelte": "^2.39.3",
"just-clone": "^6.2.0", "just-clone": "^6.2.0",
"just-debounce-it": "^3.2.0", "just-debounce-it": "^3.2.0",
"postcss": "^8.4.38", "postcss": "^8.4.38",
@ -59,11 +59,11 @@
"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.5.1", "sveltekit-rate-limiter": "^0.5.1",
"sveltekit-superforms": "^2.14.0", "sveltekit-superforms": "^2.15.1",
"tailwindcss": "^3.4.4", "tailwindcss": "^3.4.4",
"ts-node": "^10.9.2", "ts-node": "^10.9.2",
"tslib": "^2.6.3", "tslib": "^2.6.3",
"tsx": "^4.14.1", "tsx": "^4.15.1",
"typescript": "^5.4.5", "typescript": "^5.4.5",
"vite": "^5.2.13", "vite": "^5.2.13",
"vitest": "^1.6.0", "vitest": "^1.6.0",

View file

@ -61,7 +61,7 @@ importers:
version: 4.29.2 version: 4.29.2
formsnap: formsnap:
specifier: ^1.0.0 specifier: ^1.0.0
version: 1.0.0(svelte@4.2.18)(sveltekit-superforms@2.14.0(@sveltejs/kit@2.5.10(@sveltejs/vite-plugin-svelte@3.1.1(svelte@4.2.18)(vite@5.2.13(@types/node@20.14.2)(sass@1.77.4)))(svelte@4.2.18)(vite@5.2.13(@types/node@20.14.2)(sass@1.77.4)))(@types/json-schema@7.0.15)(esbuild-runner@2.2.2(esbuild@0.19.12))(esbuild@0.19.12)(svelte@4.2.18)) version: 1.0.0(svelte@4.2.18)(sveltekit-superforms@2.15.1(@sveltejs/kit@2.5.10(@sveltejs/vite-plugin-svelte@3.1.1(svelte@4.2.18)(vite@5.2.13(@types/node@20.14.2)(sass@1.77.4)))(svelte@4.2.18)(vite@5.2.13(@types/node@20.14.2)(sass@1.77.4)))(svelte@4.2.18))
html-entities: html-entities:
specifier: ^2.5.2 specifier: ^2.5.2
version: 2.5.2 version: 2.5.2
@ -166,8 +166,8 @@ importers:
specifier: ^16.4.5 specifier: ^16.4.5
version: 16.4.5 version: 16.4.5
drizzle-kit: drizzle-kit:
specifier: ^0.22.6 specifier: ^0.22.7
version: 0.22.6 version: 0.22.7
eslint: eslint:
specifier: ^8.57.0 specifier: ^8.57.0
version: 8.57.0 version: 8.57.0
@ -175,8 +175,8 @@ importers:
specifier: ^9.1.0 specifier: ^9.1.0
version: 9.1.0(eslint@8.57.0) version: 9.1.0(eslint@8.57.0)
eslint-plugin-svelte: eslint-plugin-svelte:
specifier: ^2.39.0 specifier: ^2.39.3
version: 2.39.0(eslint@8.57.0)(svelte@4.2.18)(ts-node@10.9.2(@types/node@20.14.2)(typescript@5.4.5)) version: 2.39.3(eslint@8.57.0)(svelte@4.2.18)(ts-node@10.9.2(@types/node@20.14.2)(typescript@5.4.5))
just-clone: just-clone:
specifier: ^6.2.0 specifier: ^6.2.0
version: 6.2.0 version: 6.2.0
@ -191,7 +191,7 @@ importers:
version: 16.1.0(postcss@8.4.38) version: 16.1.0(postcss@8.4.38)
postcss-load-config: postcss-load-config:
specifier: ^5.1.0 specifier: ^5.1.0
version: 5.1.0(jiti@1.21.3)(postcss@8.4.38)(tsx@4.14.1) version: 5.1.0(jiti@1.21.3)(postcss@8.4.38)(tsx@4.15.1)
postcss-preset-env: postcss-preset-env:
specifier: ^9.5.14 specifier: ^9.5.14
version: 9.5.14(postcss@8.4.38) version: 9.5.14(postcss@8.4.38)
@ -215,7 +215,7 @@ importers:
version: 4.2.18 version: 4.2.18
svelte-check: svelte-check:
specifier: ^3.8.0 specifier: ^3.8.0
version: 3.8.0(postcss-load-config@5.1.0(jiti@1.21.3)(postcss@8.4.38)(tsx@4.14.1))(postcss@8.4.38)(sass@1.77.4)(svelte@4.2.18) version: 3.8.0(postcss-load-config@5.1.0(jiti@1.21.3)(postcss@8.4.38)(tsx@4.15.1))(postcss@8.4.38)(sass@1.77.4)(svelte@4.2.18)
svelte-headless-table: svelte-headless-table:
specifier: ^0.18.2 specifier: ^0.18.2
version: 0.18.2(svelte@4.2.18) version: 0.18.2(svelte@4.2.18)
@ -224,7 +224,7 @@ importers:
version: 3.1.2(svelte@4.2.18)(typescript@5.4.5) version: 3.1.2(svelte@4.2.18)(typescript@5.4.5)
svelte-preprocess: svelte-preprocess:
specifier: ^5.1.4 specifier: ^5.1.4
version: 5.1.4(postcss-load-config@5.1.0(jiti@1.21.3)(postcss@8.4.38)(tsx@4.14.1))(postcss@8.4.38)(sass@1.77.4)(svelte@4.2.18)(typescript@5.4.5) version: 5.1.4(postcss-load-config@5.1.0(jiti@1.21.3)(postcss@8.4.38)(tsx@4.15.1))(postcss@8.4.38)(sass@1.77.4)(svelte@4.2.18)(typescript@5.4.5)
svelte-sequential-preprocessor: svelte-sequential-preprocessor:
specifier: ^2.0.1 specifier: ^2.0.1
version: 2.0.1 version: 2.0.1
@ -235,8 +235,8 @@ importers:
specifier: ^0.5.1 specifier: ^0.5.1
version: 0.5.1(@sveltejs/kit@2.5.10(@sveltejs/vite-plugin-svelte@3.1.1(svelte@4.2.18)(vite@5.2.13(@types/node@20.14.2)(sass@1.77.4)))(svelte@4.2.18)(vite@5.2.13(@types/node@20.14.2)(sass@1.77.4))) version: 0.5.1(@sveltejs/kit@2.5.10(@sveltejs/vite-plugin-svelte@3.1.1(svelte@4.2.18)(vite@5.2.13(@types/node@20.14.2)(sass@1.77.4)))(svelte@4.2.18)(vite@5.2.13(@types/node@20.14.2)(sass@1.77.4)))
sveltekit-superforms: sveltekit-superforms:
specifier: ^2.14.0 specifier: ^2.15.1
version: 2.14.0(@sveltejs/kit@2.5.10(@sveltejs/vite-plugin-svelte@3.1.1(svelte@4.2.18)(vite@5.2.13(@types/node@20.14.2)(sass@1.77.4)))(svelte@4.2.18)(vite@5.2.13(@types/node@20.14.2)(sass@1.77.4)))(@types/json-schema@7.0.15)(esbuild-runner@2.2.2(esbuild@0.19.12))(esbuild@0.19.12)(svelte@4.2.18) version: 2.15.1(@sveltejs/kit@2.5.10(@sveltejs/vite-plugin-svelte@3.1.1(svelte@4.2.18)(vite@5.2.13(@types/node@20.14.2)(sass@1.77.4)))(svelte@4.2.18)(vite@5.2.13(@types/node@20.14.2)(sass@1.77.4)))(svelte@4.2.18)
tailwindcss: tailwindcss:
specifier: ^3.4.4 specifier: ^3.4.4
version: 3.4.4(ts-node@10.9.2(@types/node@20.14.2)(typescript@5.4.5)) version: 3.4.4(ts-node@10.9.2(@types/node@20.14.2)(typescript@5.4.5))
@ -247,8 +247,8 @@ importers:
specifier: ^2.6.3 specifier: ^2.6.3
version: 2.6.3 version: 2.6.3
tsx: tsx:
specifier: ^4.14.1 specifier: ^4.15.1
version: 4.14.1 version: 4.15.1
typescript: typescript:
specifier: ^5.4.5 specifier: ^5.4.5
version: 5.4.5 version: 5.4.5
@ -272,11 +272,11 @@ packages:
resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==}
engines: {node: '>=6.0.0'} engines: {node: '>=6.0.0'}
'@arktype/schema@0.1.7': '@arktype/schema@0.1.13':
resolution: {integrity: sha512-xeaS/0EiuT5kUQGC9DqLv0vnjFpbEILmaBQF9CrLhamR0v8c+eUNM6z5u6DgzqPZbDFMmtfiCdikUTT1VueWXw==} resolution: {integrity: sha512-qZjtCAKrnhsixDWsEGJtosWfi4bLpAg4OnnICVYTer/6v5hwlhsdYpYobTSJUc5eiBoI5Ai/kcNfYaQISshY2g==}
'@arktype/util@0.0.45': '@arktype/util@0.0.48':
resolution: {integrity: sha512-WPzoElBZK1NxYzT8PnoNsnulohgRU7PRKkJUoqeGvuFqP/Egv7tRNnvcJCE0MboHUnWaPTy/5Psjm/4iOvbWiw==} resolution: {integrity: sha512-U5FO5EUAJ4LoYtLSyAMmTf6CEVgslObfSQuua2zoK5Tv2FB3aESVQ3rdLfhuz+coRhlzlynbkmimyoQWwQT+aQ==}
'@babel/runtime@7.24.5': '@babel/runtime@7.24.5':
resolution: {integrity: sha512-Nms86NXrsaeU9vbBJKni6gXiEXZ4CVpYVzEjDH9Sb8vmZ3UljyA1GSOJl/6LGPO8EHLuSF9H+IxNXHPX8QHJ4g==} resolution: {integrity: sha512-Nms86NXrsaeU9vbBJKni6gXiEXZ4CVpYVzEjDH9Sb8vmZ3UljyA1GSOJl/6LGPO8EHLuSF9H+IxNXHPX8QHJ4g==}
@ -547,6 +547,12 @@ packages:
cpu: [ppc64] cpu: [ppc64]
os: [aix] os: [aix]
'@esbuild/aix-ppc64@0.21.5':
resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==}
engines: {node: '>=12'}
cpu: [ppc64]
os: [aix]
'@esbuild/android-arm64@0.18.20': '@esbuild/android-arm64@0.18.20':
resolution: {integrity: sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==} resolution: {integrity: sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==}
engines: {node: '>=12'} engines: {node: '>=12'}
@ -565,6 +571,12 @@ packages:
cpu: [arm64] cpu: [arm64]
os: [android] os: [android]
'@esbuild/android-arm64@0.21.5':
resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==}
engines: {node: '>=12'}
cpu: [arm64]
os: [android]
'@esbuild/android-arm@0.18.20': '@esbuild/android-arm@0.18.20':
resolution: {integrity: sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==} resolution: {integrity: sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==}
engines: {node: '>=12'} engines: {node: '>=12'}
@ -583,6 +595,12 @@ packages:
cpu: [arm] cpu: [arm]
os: [android] os: [android]
'@esbuild/android-arm@0.21.5':
resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==}
engines: {node: '>=12'}
cpu: [arm]
os: [android]
'@esbuild/android-x64@0.18.20': '@esbuild/android-x64@0.18.20':
resolution: {integrity: sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==} resolution: {integrity: sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==}
engines: {node: '>=12'} engines: {node: '>=12'}
@ -601,6 +619,12 @@ packages:
cpu: [x64] cpu: [x64]
os: [android] os: [android]
'@esbuild/android-x64@0.21.5':
resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==}
engines: {node: '>=12'}
cpu: [x64]
os: [android]
'@esbuild/darwin-arm64@0.18.20': '@esbuild/darwin-arm64@0.18.20':
resolution: {integrity: sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==} resolution: {integrity: sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==}
engines: {node: '>=12'} engines: {node: '>=12'}
@ -619,6 +643,12 @@ packages:
cpu: [arm64] cpu: [arm64]
os: [darwin] os: [darwin]
'@esbuild/darwin-arm64@0.21.5':
resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==}
engines: {node: '>=12'}
cpu: [arm64]
os: [darwin]
'@esbuild/darwin-x64@0.18.20': '@esbuild/darwin-x64@0.18.20':
resolution: {integrity: sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==} resolution: {integrity: sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==}
engines: {node: '>=12'} engines: {node: '>=12'}
@ -637,6 +667,12 @@ packages:
cpu: [x64] cpu: [x64]
os: [darwin] os: [darwin]
'@esbuild/darwin-x64@0.21.5':
resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==}
engines: {node: '>=12'}
cpu: [x64]
os: [darwin]
'@esbuild/freebsd-arm64@0.18.20': '@esbuild/freebsd-arm64@0.18.20':
resolution: {integrity: sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==} resolution: {integrity: sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==}
engines: {node: '>=12'} engines: {node: '>=12'}
@ -655,6 +691,12 @@ packages:
cpu: [arm64] cpu: [arm64]
os: [freebsd] os: [freebsd]
'@esbuild/freebsd-arm64@0.21.5':
resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==}
engines: {node: '>=12'}
cpu: [arm64]
os: [freebsd]
'@esbuild/freebsd-x64@0.18.20': '@esbuild/freebsd-x64@0.18.20':
resolution: {integrity: sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==} resolution: {integrity: sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==}
engines: {node: '>=12'} engines: {node: '>=12'}
@ -673,6 +715,12 @@ packages:
cpu: [x64] cpu: [x64]
os: [freebsd] os: [freebsd]
'@esbuild/freebsd-x64@0.21.5':
resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==}
engines: {node: '>=12'}
cpu: [x64]
os: [freebsd]
'@esbuild/linux-arm64@0.18.20': '@esbuild/linux-arm64@0.18.20':
resolution: {integrity: sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==} resolution: {integrity: sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==}
engines: {node: '>=12'} engines: {node: '>=12'}
@ -691,6 +739,12 @@ packages:
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
'@esbuild/linux-arm64@0.21.5':
resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==}
engines: {node: '>=12'}
cpu: [arm64]
os: [linux]
'@esbuild/linux-arm@0.18.20': '@esbuild/linux-arm@0.18.20':
resolution: {integrity: sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==} resolution: {integrity: sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==}
engines: {node: '>=12'} engines: {node: '>=12'}
@ -709,6 +763,12 @@ packages:
cpu: [arm] cpu: [arm]
os: [linux] os: [linux]
'@esbuild/linux-arm@0.21.5':
resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==}
engines: {node: '>=12'}
cpu: [arm]
os: [linux]
'@esbuild/linux-ia32@0.18.20': '@esbuild/linux-ia32@0.18.20':
resolution: {integrity: sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==} resolution: {integrity: sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==}
engines: {node: '>=12'} engines: {node: '>=12'}
@ -727,6 +787,12 @@ packages:
cpu: [ia32] cpu: [ia32]
os: [linux] os: [linux]
'@esbuild/linux-ia32@0.21.5':
resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==}
engines: {node: '>=12'}
cpu: [ia32]
os: [linux]
'@esbuild/linux-loong64@0.18.20': '@esbuild/linux-loong64@0.18.20':
resolution: {integrity: sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==} resolution: {integrity: sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==}
engines: {node: '>=12'} engines: {node: '>=12'}
@ -745,6 +811,12 @@ packages:
cpu: [loong64] cpu: [loong64]
os: [linux] os: [linux]
'@esbuild/linux-loong64@0.21.5':
resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==}
engines: {node: '>=12'}
cpu: [loong64]
os: [linux]
'@esbuild/linux-mips64el@0.18.20': '@esbuild/linux-mips64el@0.18.20':
resolution: {integrity: sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==} resolution: {integrity: sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==}
engines: {node: '>=12'} engines: {node: '>=12'}
@ -763,6 +835,12 @@ packages:
cpu: [mips64el] cpu: [mips64el]
os: [linux] os: [linux]
'@esbuild/linux-mips64el@0.21.5':
resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==}
engines: {node: '>=12'}
cpu: [mips64el]
os: [linux]
'@esbuild/linux-ppc64@0.18.20': '@esbuild/linux-ppc64@0.18.20':
resolution: {integrity: sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==} resolution: {integrity: sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==}
engines: {node: '>=12'} engines: {node: '>=12'}
@ -781,6 +859,12 @@ packages:
cpu: [ppc64] cpu: [ppc64]
os: [linux] os: [linux]
'@esbuild/linux-ppc64@0.21.5':
resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==}
engines: {node: '>=12'}
cpu: [ppc64]
os: [linux]
'@esbuild/linux-riscv64@0.18.20': '@esbuild/linux-riscv64@0.18.20':
resolution: {integrity: sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==} resolution: {integrity: sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==}
engines: {node: '>=12'} engines: {node: '>=12'}
@ -799,6 +883,12 @@ packages:
cpu: [riscv64] cpu: [riscv64]
os: [linux] os: [linux]
'@esbuild/linux-riscv64@0.21.5':
resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==}
engines: {node: '>=12'}
cpu: [riscv64]
os: [linux]
'@esbuild/linux-s390x@0.18.20': '@esbuild/linux-s390x@0.18.20':
resolution: {integrity: sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==} resolution: {integrity: sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==}
engines: {node: '>=12'} engines: {node: '>=12'}
@ -817,6 +907,12 @@ packages:
cpu: [s390x] cpu: [s390x]
os: [linux] os: [linux]
'@esbuild/linux-s390x@0.21.5':
resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==}
engines: {node: '>=12'}
cpu: [s390x]
os: [linux]
'@esbuild/linux-x64@0.18.20': '@esbuild/linux-x64@0.18.20':
resolution: {integrity: sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==} resolution: {integrity: sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==}
engines: {node: '>=12'} engines: {node: '>=12'}
@ -835,6 +931,12 @@ packages:
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
'@esbuild/linux-x64@0.21.5':
resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==}
engines: {node: '>=12'}
cpu: [x64]
os: [linux]
'@esbuild/netbsd-x64@0.18.20': '@esbuild/netbsd-x64@0.18.20':
resolution: {integrity: sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==} resolution: {integrity: sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==}
engines: {node: '>=12'} engines: {node: '>=12'}
@ -853,6 +955,12 @@ packages:
cpu: [x64] cpu: [x64]
os: [netbsd] os: [netbsd]
'@esbuild/netbsd-x64@0.21.5':
resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==}
engines: {node: '>=12'}
cpu: [x64]
os: [netbsd]
'@esbuild/openbsd-x64@0.18.20': '@esbuild/openbsd-x64@0.18.20':
resolution: {integrity: sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==} resolution: {integrity: sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==}
engines: {node: '>=12'} engines: {node: '>=12'}
@ -871,6 +979,12 @@ packages:
cpu: [x64] cpu: [x64]
os: [openbsd] os: [openbsd]
'@esbuild/openbsd-x64@0.21.5':
resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==}
engines: {node: '>=12'}
cpu: [x64]
os: [openbsd]
'@esbuild/sunos-x64@0.18.20': '@esbuild/sunos-x64@0.18.20':
resolution: {integrity: sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==} resolution: {integrity: sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==}
engines: {node: '>=12'} engines: {node: '>=12'}
@ -889,6 +1003,12 @@ packages:
cpu: [x64] cpu: [x64]
os: [sunos] os: [sunos]
'@esbuild/sunos-x64@0.21.5':
resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==}
engines: {node: '>=12'}
cpu: [x64]
os: [sunos]
'@esbuild/win32-arm64@0.18.20': '@esbuild/win32-arm64@0.18.20':
resolution: {integrity: sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==} resolution: {integrity: sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==}
engines: {node: '>=12'} engines: {node: '>=12'}
@ -907,6 +1027,12 @@ packages:
cpu: [arm64] cpu: [arm64]
os: [win32] os: [win32]
'@esbuild/win32-arm64@0.21.5':
resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==}
engines: {node: '>=12'}
cpu: [arm64]
os: [win32]
'@esbuild/win32-ia32@0.18.20': '@esbuild/win32-ia32@0.18.20':
resolution: {integrity: sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==} resolution: {integrity: sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==}
engines: {node: '>=12'} engines: {node: '>=12'}
@ -925,6 +1051,12 @@ packages:
cpu: [ia32] cpu: [ia32]
os: [win32] os: [win32]
'@esbuild/win32-ia32@0.21.5':
resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==}
engines: {node: '>=12'}
cpu: [ia32]
os: [win32]
'@esbuild/win32-x64@0.18.20': '@esbuild/win32-x64@0.18.20':
resolution: {integrity: sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==} resolution: {integrity: sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==}
engines: {node: '>=12'} engines: {node: '>=12'}
@ -943,6 +1075,12 @@ packages:
cpu: [x64] cpu: [x64]
os: [win32] os: [win32]
'@esbuild/win32-x64@0.21.5':
resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==}
engines: {node: '>=12'}
cpu: [x64]
os: [win32]
'@eslint-community/eslint-utils@4.4.0': '@eslint-community/eslint-utils@4.4.0':
resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
@ -979,14 +1117,9 @@ packages:
'@fontsource/fira-mono@5.0.13': '@fontsource/fira-mono@5.0.13':
resolution: {integrity: sha512-fZDjR2BdAqmauEbTjcIT62zYzbOgDa5+IQH34D2k8Pxmy1T815mAqQkZciWZVQ9dc/BgdTtTUV9HJ2ulBNwchg==} resolution: {integrity: sha512-fZDjR2BdAqmauEbTjcIT62zYzbOgDa5+IQH34D2k8Pxmy1T815mAqQkZciWZVQ9dc/BgdTtTUV9HJ2ulBNwchg==}
'@gcornut/valibot-json-schema@0.0.27': '@gcornut/valibot-json-schema@0.31.0':
resolution: {integrity: sha512-xcMaUStVgQzPrK3d7PuLFbQ+3qSp6LzaLExAm52E3FKmUfjQa7Sw5cDK6Hfu/8WT0yfGsuSCuJ5uT1sosjR9Qg==} resolution: {integrity: sha512-3xGptCurm23e7nuPQkdrE5rEs1FeTPHhAUsBuwwqG4/YeZLwJOoYZv+fmsppUEfo5y9lzUwNQrNqLS/q7HMc7g==}
hasBin: true hasBin: true
peerDependencies:
'@types/json-schema': '>= 7.0.14'
esbuild: '>= 0.18.20'
esbuild-runner: '>= 2.2.2'
valibot: '>= 0.21.0'
'@hapi/hoek@9.3.0': '@hapi/hoek@9.3.0':
resolution: {integrity: sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==} resolution: {integrity: sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==}
@ -1935,8 +2068,8 @@ packages:
aria-query@5.3.0: aria-query@5.3.0:
resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==} resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==}
arktype@2.0.0-dev.15: arktype@2.0.0-dev.21:
resolution: {integrity: sha512-V8/jyfU/ISl9uSzTNZMgj/sYOI1QNxhVaqcS+spWamF/jx4eDFMqBVdhAGysJhKyLC+Qi2yNw5f1YQuaOMEeGw==} resolution: {integrity: sha512-dgHCjb3FK4BGvG2LuXqgdWXstbFmiYowSy0jiKnyk4KVcMT5DyIJ9d1nbQM3ztiAL3hIPmPdkmpfxUqR+BoOBQ==}
array-union@2.1.0: array-union@2.1.0:
resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==}
@ -2242,8 +2375,8 @@ packages:
resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==} resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==}
engines: {node: '>=12'} engines: {node: '>=12'}
drizzle-kit@0.22.6: drizzle-kit@0.22.7:
resolution: {integrity: sha512-TE4mBMeVJvySjYxwKeUOMrzytFSKnpMyp6kBnm+aascu9P+hCMrGvnJhOAO94C3wmt144CoHZQWQCOoP/EFl8A==} resolution: {integrity: sha512-9THPCb2l1GPt7wxhws9LvTR0YG565ZlVgTuqGMwjs590Kch1pXu4GyjEArVijSF5m0OBj3qgdeKmuJXhKXgWFw==}
hasBin: true hasBin: true
drizzle-orm@0.31.2: drizzle-orm@0.31.2:
@ -2376,6 +2509,11 @@ packages:
engines: {node: '>=12'} engines: {node: '>=12'}
hasBin: true hasBin: true
esbuild@0.21.5:
resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==}
engines: {node: '>=12'}
hasBin: true
escalade@3.1.2: escalade@3.1.2:
resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==} resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==}
engines: {node: '>=6'} engines: {node: '>=6'}
@ -2399,8 +2537,8 @@ packages:
peerDependencies: peerDependencies:
eslint: '>=7.0.0' eslint: '>=7.0.0'
eslint-plugin-svelte@2.39.0: eslint-plugin-svelte@2.39.3:
resolution: {integrity: sha512-FXktBLXsrxbA+6ZvJK2z/sQOrUKyzSg3fNWK5h0reSCjr2fjAsc9ai/s/JvSl4Hgvz3nYVtTIMwarZH5RcB7BA==} resolution: {integrity: sha512-uXsHW+VOSHRI3VgoDit4CURKos9wDque6CuaBNw8z6UyF5Rfc2XHmNEsRvvOd+VmMUtS+wC9bvwArv2tt4TFGA==}
engines: {node: ^14.17.0 || >=16.0.0} engines: {node: ^14.17.0 || >=16.0.0}
peerDependencies: peerDependencies:
eslint: ^7.0.0 || ^8.0.0-0 || ^9.0.0-0 eslint: ^7.0.0 || ^8.0.0-0 || ^9.0.0-0
@ -3713,8 +3851,8 @@ packages:
peerDependencies: peerDependencies:
svelte: ^3.55.0 || ^4.0.0-next.0 || ^4.0.0 || ^5.0.0-next.0 svelte: ^3.55.0 || ^4.0.0-next.0 || ^4.0.0 || ^5.0.0-next.0
svelte-eslint-parser@0.36.0: svelte-eslint-parser@0.37.0:
resolution: {integrity: sha512-/6YmUSr0FAVxW8dXNdIMydBnddPMHzaHirAZ7RrT21XYdgGGZMh0LQG6CZsvAFS4r2Y4ItUuCQc8TQ3urB30mQ==} resolution: {integrity: sha512-AXd5ar7dcOK+H86JomxcSaWevhs2J7o/xOwg+kDQu98uuATpm+tE5Twp7u8UQCdbWKB34Idu/CZyHmTOxfSQMw==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies: peerDependencies:
svelte: ^3.37.0 || ^4.0.0 || ^5.0.0-next.115 svelte: ^3.37.0 || ^4.0.0 || ^5.0.0-next.115
@ -3827,8 +3965,8 @@ packages:
peerDependencies: peerDependencies:
'@sveltejs/kit': 1.x || 2.x '@sveltejs/kit': 1.x || 2.x
sveltekit-superforms@2.14.0: sveltekit-superforms@2.15.1:
resolution: {integrity: sha512-TRN+x2+ENCnvDw70U5HLfmGQGFi4kpevpWaPpQ06AB0Wf5qCYxshbZBofMAXb8KOyetw8dhWpj86AQRPNwhzDg==} resolution: {integrity: sha512-rLzcJTGEzt2oFC1fNYn+ddM25uoCawudHBU7qoLo5gp/JLMRNhtX9gbBMt8imMLo4VcB8339VtxBRcWiMV1faQ==}
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
@ -3939,8 +4077,8 @@ packages:
tslib@2.6.3: tslib@2.6.3:
resolution: {integrity: sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==} resolution: {integrity: sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==}
tsx@4.14.1: tsx@4.15.1:
resolution: {integrity: sha512-GU8pPJq8DdxcJDSK6Bc64c2jW8zBK2hb0jzwHZDfjapbwu6AqvFnAElnzZ17Xb9TH5a/j6/sicTCVYF+eO/cmA==} resolution: {integrity: sha512-k/6h17jA1KfUR7SpcteOa880zGmF56s8gMIcSqUR5avyNFi9nlCEKpMiHLrzrqyARGr52A/JablmGey1DEWbCA==}
engines: {node: '>=18.0.0'} engines: {node: '>=18.0.0'}
hasBin: true hasBin: true
@ -3995,8 +4133,8 @@ packages:
v8-compile-cache-lib@3.0.1: v8-compile-cache-lib@3.0.1:
resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==}
valibot@0.30.0: valibot@0.31.1:
resolution: {integrity: sha512-5POBdbSkM+3nvJ6ZlyQHsggisfRtyT4tVTo1EIIShs6qCdXJnyWU5TJ68vr8iTg5zpOLjXLRiBqNx+9zwZz/rA==} resolution: {integrity: sha512-2YYIhPrnVSz/gfT2/iXVTrSj92HwchCt9Cga/6hX4B26iCz9zkIsGTS0HjDYTZfTi1Un0X6aRvhBi1cfqs/i0Q==}
validator@13.11.0: validator@13.11.0:
resolution: {integrity: sha512-Ii+sehpSfZy+At5nPdnyMhx78fEoPDkR2XW/zimHEL3MyGJQOCQ7WeP20jPYRz7ZCpcKLB21NxuXHF3bxjStBQ==} resolution: {integrity: sha512-Ii+sehpSfZy+At5nPdnyMhx78fEoPDkR2XW/zimHEL3MyGJQOCQ7WeP20jPYRz7ZCpcKLB21NxuXHF3bxjStBQ==}
@ -4180,12 +4318,12 @@ snapshots:
'@jridgewell/gen-mapping': 0.3.5 '@jridgewell/gen-mapping': 0.3.5
'@jridgewell/trace-mapping': 0.3.25 '@jridgewell/trace-mapping': 0.3.25
'@arktype/schema@0.1.7': '@arktype/schema@0.1.13':
dependencies: dependencies:
'@arktype/util': 0.0.45 '@arktype/util': 0.0.48
optional: true optional: true
'@arktype/util@0.0.45': '@arktype/util@0.0.48':
optional: true optional: true
'@babel/runtime@7.24.5': '@babel/runtime@7.24.5':
@ -4457,6 +4595,9 @@ snapshots:
'@esbuild/aix-ppc64@0.20.2': '@esbuild/aix-ppc64@0.20.2':
optional: true optional: true
'@esbuild/aix-ppc64@0.21.5':
optional: true
'@esbuild/android-arm64@0.18.20': '@esbuild/android-arm64@0.18.20':
optional: true optional: true
@ -4466,6 +4607,9 @@ snapshots:
'@esbuild/android-arm64@0.20.2': '@esbuild/android-arm64@0.20.2':
optional: true optional: true
'@esbuild/android-arm64@0.21.5':
optional: true
'@esbuild/android-arm@0.18.20': '@esbuild/android-arm@0.18.20':
optional: true optional: true
@ -4475,6 +4619,9 @@ snapshots:
'@esbuild/android-arm@0.20.2': '@esbuild/android-arm@0.20.2':
optional: true optional: true
'@esbuild/android-arm@0.21.5':
optional: true
'@esbuild/android-x64@0.18.20': '@esbuild/android-x64@0.18.20':
optional: true optional: true
@ -4484,6 +4631,9 @@ snapshots:
'@esbuild/android-x64@0.20.2': '@esbuild/android-x64@0.20.2':
optional: true optional: true
'@esbuild/android-x64@0.21.5':
optional: true
'@esbuild/darwin-arm64@0.18.20': '@esbuild/darwin-arm64@0.18.20':
optional: true optional: true
@ -4493,6 +4643,9 @@ snapshots:
'@esbuild/darwin-arm64@0.20.2': '@esbuild/darwin-arm64@0.20.2':
optional: true optional: true
'@esbuild/darwin-arm64@0.21.5':
optional: true
'@esbuild/darwin-x64@0.18.20': '@esbuild/darwin-x64@0.18.20':
optional: true optional: true
@ -4502,6 +4655,9 @@ snapshots:
'@esbuild/darwin-x64@0.20.2': '@esbuild/darwin-x64@0.20.2':
optional: true optional: true
'@esbuild/darwin-x64@0.21.5':
optional: true
'@esbuild/freebsd-arm64@0.18.20': '@esbuild/freebsd-arm64@0.18.20':
optional: true optional: true
@ -4511,6 +4667,9 @@ snapshots:
'@esbuild/freebsd-arm64@0.20.2': '@esbuild/freebsd-arm64@0.20.2':
optional: true optional: true
'@esbuild/freebsd-arm64@0.21.5':
optional: true
'@esbuild/freebsd-x64@0.18.20': '@esbuild/freebsd-x64@0.18.20':
optional: true optional: true
@ -4520,6 +4679,9 @@ snapshots:
'@esbuild/freebsd-x64@0.20.2': '@esbuild/freebsd-x64@0.20.2':
optional: true optional: true
'@esbuild/freebsd-x64@0.21.5':
optional: true
'@esbuild/linux-arm64@0.18.20': '@esbuild/linux-arm64@0.18.20':
optional: true optional: true
@ -4529,6 +4691,9 @@ snapshots:
'@esbuild/linux-arm64@0.20.2': '@esbuild/linux-arm64@0.20.2':
optional: true optional: true
'@esbuild/linux-arm64@0.21.5':
optional: true
'@esbuild/linux-arm@0.18.20': '@esbuild/linux-arm@0.18.20':
optional: true optional: true
@ -4538,6 +4703,9 @@ snapshots:
'@esbuild/linux-arm@0.20.2': '@esbuild/linux-arm@0.20.2':
optional: true optional: true
'@esbuild/linux-arm@0.21.5':
optional: true
'@esbuild/linux-ia32@0.18.20': '@esbuild/linux-ia32@0.18.20':
optional: true optional: true
@ -4547,6 +4715,9 @@ snapshots:
'@esbuild/linux-ia32@0.20.2': '@esbuild/linux-ia32@0.20.2':
optional: true optional: true
'@esbuild/linux-ia32@0.21.5':
optional: true
'@esbuild/linux-loong64@0.18.20': '@esbuild/linux-loong64@0.18.20':
optional: true optional: true
@ -4556,6 +4727,9 @@ snapshots:
'@esbuild/linux-loong64@0.20.2': '@esbuild/linux-loong64@0.20.2':
optional: true optional: true
'@esbuild/linux-loong64@0.21.5':
optional: true
'@esbuild/linux-mips64el@0.18.20': '@esbuild/linux-mips64el@0.18.20':
optional: true optional: true
@ -4565,6 +4739,9 @@ snapshots:
'@esbuild/linux-mips64el@0.20.2': '@esbuild/linux-mips64el@0.20.2':
optional: true optional: true
'@esbuild/linux-mips64el@0.21.5':
optional: true
'@esbuild/linux-ppc64@0.18.20': '@esbuild/linux-ppc64@0.18.20':
optional: true optional: true
@ -4574,6 +4751,9 @@ snapshots:
'@esbuild/linux-ppc64@0.20.2': '@esbuild/linux-ppc64@0.20.2':
optional: true optional: true
'@esbuild/linux-ppc64@0.21.5':
optional: true
'@esbuild/linux-riscv64@0.18.20': '@esbuild/linux-riscv64@0.18.20':
optional: true optional: true
@ -4583,6 +4763,9 @@ snapshots:
'@esbuild/linux-riscv64@0.20.2': '@esbuild/linux-riscv64@0.20.2':
optional: true optional: true
'@esbuild/linux-riscv64@0.21.5':
optional: true
'@esbuild/linux-s390x@0.18.20': '@esbuild/linux-s390x@0.18.20':
optional: true optional: true
@ -4592,6 +4775,9 @@ snapshots:
'@esbuild/linux-s390x@0.20.2': '@esbuild/linux-s390x@0.20.2':
optional: true optional: true
'@esbuild/linux-s390x@0.21.5':
optional: true
'@esbuild/linux-x64@0.18.20': '@esbuild/linux-x64@0.18.20':
optional: true optional: true
@ -4601,6 +4787,9 @@ snapshots:
'@esbuild/linux-x64@0.20.2': '@esbuild/linux-x64@0.20.2':
optional: true optional: true
'@esbuild/linux-x64@0.21.5':
optional: true
'@esbuild/netbsd-x64@0.18.20': '@esbuild/netbsd-x64@0.18.20':
optional: true optional: true
@ -4610,6 +4799,9 @@ snapshots:
'@esbuild/netbsd-x64@0.20.2': '@esbuild/netbsd-x64@0.20.2':
optional: true optional: true
'@esbuild/netbsd-x64@0.21.5':
optional: true
'@esbuild/openbsd-x64@0.18.20': '@esbuild/openbsd-x64@0.18.20':
optional: true optional: true
@ -4619,6 +4811,9 @@ snapshots:
'@esbuild/openbsd-x64@0.20.2': '@esbuild/openbsd-x64@0.20.2':
optional: true optional: true
'@esbuild/openbsd-x64@0.21.5':
optional: true
'@esbuild/sunos-x64@0.18.20': '@esbuild/sunos-x64@0.18.20':
optional: true optional: true
@ -4628,6 +4823,9 @@ snapshots:
'@esbuild/sunos-x64@0.20.2': '@esbuild/sunos-x64@0.20.2':
optional: true optional: true
'@esbuild/sunos-x64@0.21.5':
optional: true
'@esbuild/win32-arm64@0.18.20': '@esbuild/win32-arm64@0.18.20':
optional: true optional: true
@ -4637,6 +4835,9 @@ snapshots:
'@esbuild/win32-arm64@0.20.2': '@esbuild/win32-arm64@0.20.2':
optional: true optional: true
'@esbuild/win32-arm64@0.21.5':
optional: true
'@esbuild/win32-ia32@0.18.20': '@esbuild/win32-ia32@0.18.20':
optional: true optional: true
@ -4646,6 +4847,9 @@ snapshots:
'@esbuild/win32-ia32@0.20.2': '@esbuild/win32-ia32@0.20.2':
optional: true optional: true
'@esbuild/win32-ia32@0.21.5':
optional: true
'@esbuild/win32-x64@0.18.20': '@esbuild/win32-x64@0.18.20':
optional: true optional: true
@ -4655,6 +4859,9 @@ snapshots:
'@esbuild/win32-x64@0.20.2': '@esbuild/win32-x64@0.20.2':
optional: true optional: true
'@esbuild/win32-x64@0.21.5':
optional: true
'@eslint-community/eslint-utils@4.4.0(eslint@8.57.0)': '@eslint-community/eslint-utils@4.4.0(eslint@8.57.0)':
dependencies: dependencies:
eslint: 8.57.0 eslint: 8.57.0
@ -4698,12 +4905,13 @@ snapshots:
'@fontsource/fira-mono@5.0.13': {} '@fontsource/fira-mono@5.0.13': {}
'@gcornut/valibot-json-schema@0.0.27(@types/json-schema@7.0.15)(esbuild-runner@2.2.2(esbuild@0.19.12))(esbuild@0.19.12)(valibot@0.30.0)': '@gcornut/valibot-json-schema@0.31.0':
dependencies: dependencies:
valibot: 0.31.1
optionalDependencies:
'@types/json-schema': 7.0.15 '@types/json-schema': 7.0.15
esbuild: 0.19.12 esbuild: 0.21.5
esbuild-runner: 2.2.2(esbuild@0.19.12) esbuild-runner: 2.2.2(esbuild@0.21.5)
valibot: 0.30.0
optional: true optional: true
'@hapi/hoek@9.3.0': '@hapi/hoek@9.3.0':
@ -5575,10 +5783,10 @@ snapshots:
dependencies: dependencies:
dequal: 2.0.3 dequal: 2.0.3
arktype@2.0.0-dev.15: arktype@2.0.0-dev.21:
dependencies: dependencies:
'@arktype/schema': 0.1.7 '@arktype/schema': 0.1.13
'@arktype/util': 0.0.45 '@arktype/util': 0.0.48
optional: true optional: true
array-union@2.1.0: {} array-union@2.1.0: {}
@ -5852,7 +6060,7 @@ snapshots:
dotenv@16.4.5: {} dotenv@16.4.5: {}
drizzle-kit@0.22.6: drizzle-kit@0.22.7:
dependencies: dependencies:
'@esbuild-kit/esm-loader': 2.6.5 '@esbuild-kit/esm-loader': 2.6.5
esbuild: 0.19.12 esbuild: 0.19.12
@ -5888,9 +6096,9 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
esbuild-runner@2.2.2(esbuild@0.19.12): esbuild-runner@2.2.2(esbuild@0.21.5):
dependencies: dependencies:
esbuild: 0.19.12 esbuild: 0.21.5
source-map-support: 0.5.21 source-map-support: 0.5.21
tslib: 2.4.0 tslib: 2.4.0
optional: true optional: true
@ -5972,6 +6180,32 @@ snapshots:
'@esbuild/win32-ia32': 0.20.2 '@esbuild/win32-ia32': 0.20.2
'@esbuild/win32-x64': 0.20.2 '@esbuild/win32-x64': 0.20.2
esbuild@0.21.5:
optionalDependencies:
'@esbuild/aix-ppc64': 0.21.5
'@esbuild/android-arm': 0.21.5
'@esbuild/android-arm64': 0.21.5
'@esbuild/android-x64': 0.21.5
'@esbuild/darwin-arm64': 0.21.5
'@esbuild/darwin-x64': 0.21.5
'@esbuild/freebsd-arm64': 0.21.5
'@esbuild/freebsd-x64': 0.21.5
'@esbuild/linux-arm': 0.21.5
'@esbuild/linux-arm64': 0.21.5
'@esbuild/linux-ia32': 0.21.5
'@esbuild/linux-loong64': 0.21.5
'@esbuild/linux-mips64el': 0.21.5
'@esbuild/linux-ppc64': 0.21.5
'@esbuild/linux-riscv64': 0.21.5
'@esbuild/linux-s390x': 0.21.5
'@esbuild/linux-x64': 0.21.5
'@esbuild/netbsd-x64': 0.21.5
'@esbuild/openbsd-x64': 0.21.5
'@esbuild/sunos-x64': 0.21.5
'@esbuild/win32-arm64': 0.21.5
'@esbuild/win32-ia32': 0.21.5
'@esbuild/win32-x64': 0.21.5
escalade@3.1.2: {} escalade@3.1.2: {}
escape-html@1.0.3: {} escape-html@1.0.3: {}
@ -5987,11 +6221,10 @@ snapshots:
dependencies: dependencies:
eslint: 8.57.0 eslint: 8.57.0
eslint-plugin-svelte@2.39.0(eslint@8.57.0)(svelte@4.2.18)(ts-node@10.9.2(@types/node@20.14.2)(typescript@5.4.5)): eslint-plugin-svelte@2.39.3(eslint@8.57.0)(svelte@4.2.18)(ts-node@10.9.2(@types/node@20.14.2)(typescript@5.4.5)):
dependencies: dependencies:
'@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0)
'@jridgewell/sourcemap-codec': 1.4.15 '@jridgewell/sourcemap-codec': 1.4.15
debug: 4.3.5
eslint: 8.57.0 eslint: 8.57.0
eslint-compat-utils: 0.5.1(eslint@8.57.0) eslint-compat-utils: 0.5.1(eslint@8.57.0)
esutils: 2.0.3 esutils: 2.0.3
@ -6001,11 +6234,10 @@ snapshots:
postcss-safe-parser: 6.0.0(postcss@8.4.38) postcss-safe-parser: 6.0.0(postcss@8.4.38)
postcss-selector-parser: 6.1.0 postcss-selector-parser: 6.1.0
semver: 7.6.2 semver: 7.6.2
svelte-eslint-parser: 0.36.0(svelte@4.2.18) svelte-eslint-parser: 0.37.0(svelte@4.2.18)
optionalDependencies: optionalDependencies:
svelte: 4.2.18 svelte: 4.2.18
transitivePeerDependencies: transitivePeerDependencies:
- supports-color
- ts-node - ts-node
eslint-scope@7.2.2: eslint-scope@7.2.2:
@ -6166,11 +6398,11 @@ snapshots:
cross-spawn: 7.0.3 cross-spawn: 7.0.3
signal-exit: 4.1.0 signal-exit: 4.1.0
formsnap@1.0.0(svelte@4.2.18)(sveltekit-superforms@2.14.0(@sveltejs/kit@2.5.10(@sveltejs/vite-plugin-svelte@3.1.1(svelte@4.2.18)(vite@5.2.13(@types/node@20.14.2)(sass@1.77.4)))(svelte@4.2.18)(vite@5.2.13(@types/node@20.14.2)(sass@1.77.4)))(@types/json-schema@7.0.15)(esbuild-runner@2.2.2(esbuild@0.19.12))(esbuild@0.19.12)(svelte@4.2.18)): formsnap@1.0.0(svelte@4.2.18)(sveltekit-superforms@2.15.1(@sveltejs/kit@2.5.10(@sveltejs/vite-plugin-svelte@3.1.1(svelte@4.2.18)(vite@5.2.13(@types/node@20.14.2)(sass@1.77.4)))(svelte@4.2.18)(vite@5.2.13(@types/node@20.14.2)(sass@1.77.4)))(svelte@4.2.18)):
dependencies: dependencies:
nanoid: 5.0.7 nanoid: 5.0.7
svelte: 4.2.18 svelte: 4.2.18
sveltekit-superforms: 2.14.0(@sveltejs/kit@2.5.10(@sveltejs/vite-plugin-svelte@3.1.1(svelte@4.2.18)(vite@5.2.13(@types/node@20.14.2)(sass@1.77.4)))(svelte@4.2.18)(vite@5.2.13(@types/node@20.14.2)(sass@1.77.4)))(@types/json-schema@7.0.15)(esbuild-runner@2.2.2(esbuild@0.19.12))(esbuild@0.19.12)(svelte@4.2.18) sveltekit-superforms: 2.15.1(@sveltejs/kit@2.5.10(@sveltejs/vite-plugin-svelte@3.1.1(svelte@4.2.18)(vite@5.2.13(@types/node@20.14.2)(sass@1.77.4)))(svelte@4.2.18)(vite@5.2.13(@types/node@20.14.2)(sass@1.77.4)))(svelte@4.2.18)
fraction.js@4.3.7: {} fraction.js@4.3.7: {}
@ -6883,14 +7115,14 @@ snapshots:
postcss: 8.4.38 postcss: 8.4.38
ts-node: 10.9.2(@types/node@20.14.2)(typescript@5.4.5) ts-node: 10.9.2(@types/node@20.14.2)(typescript@5.4.5)
postcss-load-config@5.1.0(jiti@1.21.3)(postcss@8.4.38)(tsx@4.14.1): postcss-load-config@5.1.0(jiti@1.21.3)(postcss@8.4.38)(tsx@4.15.1):
dependencies: dependencies:
lilconfig: 3.1.1 lilconfig: 3.1.1
yaml: 2.4.2 yaml: 2.4.2
optionalDependencies: optionalDependencies:
jiti: 1.21.3 jiti: 1.21.3
postcss: 8.4.38 postcss: 8.4.38
tsx: 4.14.1 tsx: 4.15.1
postcss-logical@7.0.1(postcss@8.4.38): postcss-logical@7.0.1(postcss@8.4.38):
dependencies: dependencies:
@ -7379,7 +7611,7 @@ snapshots:
supports-preserve-symlinks-flag@1.0.0: {} supports-preserve-symlinks-flag@1.0.0: {}
svelte-check@3.8.0(postcss-load-config@5.1.0(jiti@1.21.3)(postcss@8.4.38)(tsx@4.14.1))(postcss@8.4.38)(sass@1.77.4)(svelte@4.2.18): svelte-check@3.8.0(postcss-load-config@5.1.0(jiti@1.21.3)(postcss@8.4.38)(tsx@4.15.1))(postcss@8.4.38)(sass@1.77.4)(svelte@4.2.18):
dependencies: dependencies:
'@jridgewell/trace-mapping': 0.3.25 '@jridgewell/trace-mapping': 0.3.25
chokidar: 3.6.0 chokidar: 3.6.0
@ -7388,7 +7620,7 @@ snapshots:
picocolors: 1.0.0 picocolors: 1.0.0
sade: 1.8.1 sade: 1.8.1
svelte: 4.2.18 svelte: 4.2.18
svelte-preprocess: 5.1.4(postcss-load-config@5.1.0(jiti@1.21.3)(postcss@8.4.38)(tsx@4.14.1))(postcss@8.4.38)(sass@1.77.4)(svelte@4.2.18)(typescript@5.4.5) svelte-preprocess: 5.1.4(postcss-load-config@5.1.0(jiti@1.21.3)(postcss@8.4.38)(tsx@4.15.1))(postcss@8.4.38)(sass@1.77.4)(svelte@4.2.18)(typescript@5.4.5)
typescript: 5.4.5 typescript: 5.4.5
transitivePeerDependencies: transitivePeerDependencies:
- '@babel/core' - '@babel/core'
@ -7401,7 +7633,7 @@ snapshots:
- stylus - stylus
- sugarss - sugarss
svelte-eslint-parser@0.36.0(svelte@4.2.18): svelte-eslint-parser@0.37.0(svelte@4.2.18):
dependencies: dependencies:
eslint-scope: 7.2.2 eslint-scope: 7.2.2
eslint-visitor-keys: 3.4.3 eslint-visitor-keys: 3.4.3
@ -7444,7 +7676,7 @@ snapshots:
dependencies: dependencies:
svelte: 4.2.18 svelte: 4.2.18
svelte-preprocess@5.1.4(postcss-load-config@5.1.0(jiti@1.21.3)(postcss@8.4.38)(tsx@4.14.1))(postcss@8.4.38)(sass@1.77.4)(svelte@4.2.18)(typescript@5.4.5): svelte-preprocess@5.1.4(postcss-load-config@5.1.0(jiti@1.21.3)(postcss@8.4.38)(tsx@4.15.1))(postcss@8.4.38)(sass@1.77.4)(svelte@4.2.18)(typescript@5.4.5):
dependencies: dependencies:
'@types/pug': 2.0.10 '@types/pug': 2.0.10
detect-indent: 6.1.0 detect-indent: 6.1.0
@ -7454,7 +7686,7 @@ snapshots:
svelte: 4.2.18 svelte: 4.2.18
optionalDependencies: optionalDependencies:
postcss: 8.4.38 postcss: 8.4.38
postcss-load-config: 5.1.0(jiti@1.21.3)(postcss@8.4.38)(tsx@4.14.1) postcss-load-config: 5.1.0(jiti@1.21.3)(postcss@8.4.38)(tsx@4.15.1)
sass: 1.77.4 sass: 1.77.4
typescript: 5.4.5 typescript: 5.4.5
@ -7503,7 +7735,7 @@ snapshots:
'@isaacs/ttlcache': 1.4.1 '@isaacs/ttlcache': 1.4.1
'@sveltejs/kit': 2.5.10(@sveltejs/vite-plugin-svelte@3.1.1(svelte@4.2.18)(vite@5.2.13(@types/node@20.14.2)(sass@1.77.4)))(svelte@4.2.18)(vite@5.2.13(@types/node@20.14.2)(sass@1.77.4)) '@sveltejs/kit': 2.5.10(@sveltejs/vite-plugin-svelte@3.1.1(svelte@4.2.18)(vite@5.2.13(@types/node@20.14.2)(sass@1.77.4)))(svelte@4.2.18)(vite@5.2.13(@types/node@20.14.2)(sass@1.77.4))
sveltekit-superforms@2.14.0(@sveltejs/kit@2.5.10(@sveltejs/vite-plugin-svelte@3.1.1(svelte@4.2.18)(vite@5.2.13(@types/node@20.14.2)(sass@1.77.4)))(svelte@4.2.18)(vite@5.2.13(@types/node@20.14.2)(sass@1.77.4)))(@types/json-schema@7.0.15)(esbuild-runner@2.2.2(esbuild@0.19.12))(esbuild@0.19.12)(svelte@4.2.18): sveltekit-superforms@2.15.1(@sveltejs/kit@2.5.10(@sveltejs/vite-plugin-svelte@3.1.1(svelte@4.2.18)(vite@5.2.13(@types/node@20.14.2)(sass@1.77.4)))(svelte@4.2.18)(vite@5.2.13(@types/node@20.14.2)(sass@1.77.4)))(svelte@4.2.18):
dependencies: dependencies:
'@sveltejs/kit': 2.5.10(@sveltejs/vite-plugin-svelte@3.1.1(svelte@4.2.18)(vite@5.2.13(@types/node@20.14.2)(sass@1.77.4)))(svelte@4.2.18)(vite@5.2.13(@types/node@20.14.2)(sass@1.77.4)) '@sveltejs/kit': 2.5.10(@sveltejs/vite-plugin-svelte@3.1.1(svelte@4.2.18)(vite@5.2.13(@types/node@20.14.2)(sass@1.77.4)))(svelte@4.2.18)(vite@5.2.13(@types/node@20.14.2)(sass@1.77.4))
devalue: 5.0.0 devalue: 5.0.0
@ -7513,22 +7745,18 @@ snapshots:
ts-deepmerge: 7.0.0 ts-deepmerge: 7.0.0
optionalDependencies: optionalDependencies:
'@exodus/schemasafe': 1.3.0 '@exodus/schemasafe': 1.3.0
'@gcornut/valibot-json-schema': 0.0.27(@types/json-schema@7.0.15)(esbuild-runner@2.2.2(esbuild@0.19.12))(esbuild@0.19.12)(valibot@0.30.0) '@gcornut/valibot-json-schema': 0.31.0
'@sinclair/typebox': 0.32.31 '@sinclair/typebox': 0.32.31
'@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
arktype: 2.0.0-dev.15 arktype: 2.0.0-dev.21
joi: 17.13.1 joi: 17.13.1
json-schema-to-ts: 3.1.0 json-schema-to-ts: 3.1.0
superstruct: 1.0.4 superstruct: 1.0.4
valibot: 0.30.0 valibot: 0.31.1
yup: 1.4.0 yup: 1.4.0
zod: 3.23.8 zod: 3.23.8
zod-to-json-schema: 3.23.0(zod@3.23.8) zod-to-json-schema: 3.23.0(zod@3.23.8)
transitivePeerDependencies:
- '@types/json-schema'
- esbuild
- esbuild-runner
tabbable@6.2.0: {} tabbable@6.2.0: {}
@ -7652,9 +7880,9 @@ snapshots:
tslib@2.6.3: {} tslib@2.6.3: {}
tsx@4.14.1: tsx@4.15.1:
dependencies: dependencies:
esbuild: 0.20.2 esbuild: 0.21.5
get-tsconfig: 4.7.5 get-tsconfig: 4.7.5
optionalDependencies: optionalDependencies:
fsevents: 2.3.3 fsevents: 2.3.3
@ -7699,7 +7927,7 @@ snapshots:
v8-compile-cache-lib@3.0.1: {} v8-compile-cache-lib@3.0.1: {}
valibot@0.30.0: valibot@0.31.1:
optional: true optional: true
validator@13.11.0: validator@13.11.0:

View file

@ -1,5 +1,6 @@
import 'dotenv/config'; import 'dotenv/config';
import postgres from 'postgres'; import postgres from 'postgres';
import config from '../../drizzle.config';
import { drizzle } from 'drizzle-orm/postgres-js'; import { drizzle } from 'drizzle-orm/postgres-js';
import { migrate } from 'drizzle-orm/postgres-js/migrator'; import { migrate } from 'drizzle-orm/postgres-js/migrator';
@ -10,12 +11,12 @@ const connection = postgres({
password: process.env.DATABASE_PASSWORD || '', password: process.env.DATABASE_PASSWORD || '',
database: process.env.DATABASE_DB || 'boredgame', database: process.env.DATABASE_DB || 'boredgame',
ssl: process.env.NODE_ENV === 'development' ? false : 'require', ssl: process.env.NODE_ENV === 'development' ? false : 'require',
max: 1 max: 1,
}); });
const db = drizzle(connection); const db = drizzle(connection);
try { try {
await migrate(db, { migrationsFolder: 'drizzle' }); await migrate(db, { migrationsFolder: config.out! });
console.log('Migrations complete'); console.log('Migrations complete');
} catch (e) { } catch (e) {
console.error(e); console.error(e);

View file

@ -52,8 +52,8 @@ CREATE TABLE IF NOT EXISTS "external_ids" (
CREATE TABLE IF NOT EXISTS "games" ( CREATE TABLE IF NOT EXISTS "games" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"cuid" text, "cuid" text,
"name" text, "name" text NOT NULL,
"slug" text, "slug" text NOT NULL,
"description" text, "description" text,
"year_published" integer, "year_published" integer,
"min_players" integer, "min_players" integer,
@ -152,7 +152,9 @@ CREATE TABLE IF NOT EXISTS "sessions" (
"user_id" uuid NOT NULL, "user_id" uuid NOT NULL,
"expires_at" timestamp with time zone NOT NULL, "expires_at" timestamp with time zone NOT NULL,
"ip_country" text, "ip_country" text,
"ip_address" text "ip_address" text,
"two_factor_auth_enabled" boolean DEFAULT false,
"is_two_factor_authenticated" boolean DEFAULT false
); );
--> statement-breakpoint --> statement-breakpoint
CREATE TABLE IF NOT EXISTS "user_roles" ( CREATE TABLE IF NOT EXISTS "user_roles" (
@ -357,6 +359,6 @@ EXCEPTION
END $$; END $$;
--> statement-breakpoint --> statement-breakpoint
CREATE INDEX IF NOT EXISTS "search_index" ON "games" USING gin (( CREATE INDEX IF NOT EXISTS "search_index" ON "games" USING gin ((
setweight(to_tsvector('english', "name"), 'A') || setweight(to_tsvector('english', "name"), 'A') ||
setweight(to_tsvector('english', "slug"), 'B') setweight(to_tsvector('english', "slug"), 'B')
)); ));

View file

@ -1 +0,0 @@
ALTER TABLE "sessions" ADD COLUMN "is_two_factor_authenticated" boolean DEFAULT false;

View file

@ -1,7 +0,0 @@
DROP INDEX IF EXISTS "search_index";--> statement-breakpoint
ALTER TABLE "games" ALTER COLUMN "name" SET NOT NULL;--> statement-breakpoint
ALTER TABLE "games" ALTER COLUMN "slug" SET NOT NULL;--> statement-breakpoint
CREATE INDEX IF NOT EXISTS "search_index" ON "games" USING gin ((
setweight(to_tsvector('english', "name"), 'A') ||
setweight(to_tsvector('english', "slug"), 'B')
));

View file

@ -1,5 +0,0 @@
DROP INDEX IF EXISTS "search_index";--> statement-breakpoint
CREATE INDEX IF NOT EXISTS "search_index" ON "games" USING gin ((
setweight(to_tsvector('english', "name"), 'A') ||
setweight(to_tsvector('english', "slug"), 'B')
));

View file

@ -1,5 +1,5 @@
{ {
"id": "9622fc3a-51a1-4f66-8535-fd1ceaf46fc2", "id": "d62b05e0-f6b5-4fad-b160-bb378e0ad3a0",
"prevId": "00000000-0000-0000-0000-000000000000", "prevId": "00000000-0000-0000-0000-000000000000",
"version": "7", "version": "7",
"dialect": "postgresql", "dialect": "postgresql",
@ -403,13 +403,13 @@
"name": "name", "name": "name",
"type": "text", "type": "text",
"primaryKey": false, "primaryKey": false,
"notNull": false "notNull": true
}, },
"slug": { "slug": {
"name": "slug", "name": "slug",
"type": "text", "type": "text",
"primaryKey": false, "primaryKey": false,
"notNull": false "notNull": true
}, },
"description": { "description": {
"name": "description", "name": "description",
@ -503,7 +503,7 @@
"name": "search_index", "name": "search_index",
"columns": [ "columns": [
{ {
"expression": "(\n\t\t\t\t\t\tsetweight(to_tsvector('english', \"name\"), 'A') ||\n\t\t\t\t\t\tsetweight(to_tsvector('english', \"slug\"), 'B')\n\t\t\t\t\t)", "expression": "(\n setweight(to_tsvector('english', \"name\"), 'A') ||\n setweight(to_tsvector('english', \"slug\"), 'B')\n )",
"asc": true, "asc": true,
"isExpression": true, "isExpression": true,
"nulls": "last" "nulls": "last"
@ -1130,6 +1130,20 @@
"type": "text", "type": "text",
"primaryKey": false, "primaryKey": false,
"notNull": false "notNull": false
},
"two_factor_auth_enabled": {
"name": "two_factor_auth_enabled",
"type": "boolean",
"primaryKey": false,
"notNull": false,
"default": false
},
"is_two_factor_authenticated": {
"name": "is_two_factor_authenticated",
"type": "boolean",
"primaryKey": false,
"notNull": false,
"default": false
} }
}, },
"indexes": {}, "indexes": {},

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -5,29 +5,8 @@
{ {
"idx": 0, "idx": 0,
"version": "7", "version": "7",
"when": 1717548517806, "when": 1718038929675,
"tag": "0000_spotty_changeling", "tag": "0000_flippant_slyde",
"breakpoints": true
},
{
"idx": 1,
"version": "7",
"when": 1717891533784,
"tag": "0001_acoustic_thunderbolt",
"breakpoints": true
},
{
"idx": 2,
"version": "7",
"when": 1717891822622,
"tag": "0002_striped_darkhawk",
"breakpoints": true
},
{
"idx": 3,
"version": "7",
"when": 1717891852881,
"tag": "0003_flippant_avengers",
"breakpoints": true "breakpoints": true
} }
] ]

View file

@ -11,8 +11,8 @@ const categories = pgTable('categories', {
.$defaultFn(() => cuid2()), .$defaultFn(() => cuid2()),
name: text('name'), name: text('name'),
slug: text('slug'), slug: text('slug'),
created_at: timestamp('created_at', { mode: 'string' }).notNull().defaultNow(), created_at: timestamp('created_at').notNull().defaultNow(),
updated_at: timestamp('updated_at', { mode: 'string' }).notNull().defaultNow(), updated_at: timestamp('updated_at').notNull().defaultNow(),
}); });
export type Categories = InferSelectModel<typeof categories>; export type Categories = InferSelectModel<typeof categories>;

View file

@ -16,8 +16,8 @@ const collection_items = pgTable('collection_items', {
.notNull() .notNull()
.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', { mode: 'string' }).notNull().defaultNow(), created_at: timestamp('created_at').notNull().defaultNow(),
updated_at: timestamp('updated_at', { mode: 'string' }).notNull().defaultNow(), updated_at: timestamp('updated_at').notNull().defaultNow(),
}); });
export type CollectionItems = InferSelectModel<typeof collection_items>; export type CollectionItems = InferSelectModel<typeof collection_items>;

View file

@ -12,8 +12,8 @@ const collections = pgTable('collections', {
.notNull() .notNull()
.references(() => users.id, { onDelete: 'cascade' }), .references(() => users.id, { onDelete: 'cascade' }),
name: text('name').notNull().default('My Collection'), name: text('name').notNull().default('My Collection'),
created_at: timestamp('created_at', { mode: 'string' }).notNull().defaultNow(), created_at: timestamp('created_at').notNull().defaultNow(),
updated_at: timestamp('updated_at', { mode: 'string' }).notNull().defaultNow(), updated_at: timestamp('updated_at').notNull().defaultNow(),
}); });
export const collection_relations = relations(collections, ({ one }) => ({ export const collection_relations = relations(collections, ({ one }) => ({

View file

@ -14,8 +14,8 @@ export const expansions = pgTable('expansions', {
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' }),
created_at: timestamp('created_at', { mode: 'string' }).notNull().defaultNow(), created_at: timestamp('created_at').notNull().defaultNow(),
updated_at: timestamp('updated_at', { mode: 'string' }).notNull().defaultNow(), updated_at: timestamp('updated_at').notNull().defaultNow(),
}); });
export type Expansions = InferSelectModel<typeof expansions>; export type Expansions = InferSelectModel<typeof expansions>;

View file

@ -27,18 +27,18 @@ const games = pgTable(
thumb_url: text('thumb_url'), thumb_url: text('thumb_url'),
url: text('url'), url: text('url'),
last_sync_at: timestamp('last_sync_at'), last_sync_at: timestamp('last_sync_at'),
created_at: timestamp('created_at', { mode: 'string' }).notNull().defaultNow(), created_at: timestamp('created_at').notNull().defaultNow(),
updated_at: timestamp('updated_at', { mode: 'string' }).notNull().defaultNow(), updated_at: timestamp('updated_at').notNull().defaultNow(),
}, },
(table) => ({ (table) => ({
searchIndex: index('search_index').using( searchIndex: index('search_index').using(
'gin', 'gin',
sql`( sql`(
setweight(to_tsvector('english', ${table.name}), 'A') || setweight(to_tsvector('english', ${table.name}), 'A') ||
setweight(to_tsvector('english', ${table.slug}), 'B') setweight(to_tsvector('english', ${table.slug}), 'B')
)` )`,
), ),
}) }),
); );
export const gameRelations = relations(games, ({ many }) => ({ export const gameRelations = relations(games, ({ many }) => ({

View file

@ -5,7 +5,7 @@ export {
password_reset_token_relations, password_reset_token_relations,
type PasswordResetTokens, type PasswordResetTokens,
} from './passwordResetTokens'; } from './passwordResetTokens';
export { default as sessions } from './sessions'; export { default as sessions, type Sessions } from './sessions';
export { default as roles, role_relations, type Roles } from './roles'; export { default as roles, role_relations, type Roles } from './roles';
export { default as userRoles, user_role_relations, type UserRoles } from './userRoles'; export { default as userRoles, user_role_relations, type UserRoles } from './userRoles';
export { default as collections, collection_relations, type Collections } from './collections'; export { default as collections, collection_relations, type Collections } from './collections';
@ -23,6 +23,7 @@ export {
export { default as externalIds, type ExternalIds, type ExternalIdType } from './externalIds'; export { default as externalIds, type ExternalIds, type ExternalIdType } from './externalIds';
export { default as games, gameRelations, type Games } from './games'; export { default as games, gameRelations, type Games } from './games';
export { default as gamesToExternalIds } from './gamesToExternalIds'; export { default as gamesToExternalIds } from './gamesToExternalIds';
export { default as expansions, expansions_relations, type Expansions } from './expansions';
export { default as publishers, publishers_relations, type Publishers } from './publishers'; export { default as publishers, publishers_relations, type Publishers } from './publishers';
export { default as publishers_to_games, publishers_to_games_relations } from './publishersToGames'; export { default as publishers_to_games, publishers_to_games_relations } from './publishersToGames';
export { default as publishersToExternalIds } from './publishersToExternalIds'; export { default as publishersToExternalIds } from './publishersToExternalIds';

View file

@ -11,8 +11,8 @@ const mechanics = pgTable('mechanics', {
.$defaultFn(() => cuid2()), .$defaultFn(() => cuid2()),
name: text('name'), name: text('name'),
slug: text('slug'), slug: text('slug'),
created_at: timestamp('created_at', { mode: 'string' }).notNull().defaultNow(), created_at: timestamp('created_at').notNull().defaultNow(),
updated_at: timestamp('updated_at', { mode: 'string' }).notNull().defaultNow(), updated_at: timestamp('updated_at').notNull().defaultNow(),
}); });
export type Mechanics = InferSelectModel<typeof mechanics>; export type Mechanics = InferSelectModel<typeof mechanics>;

View file

@ -10,8 +10,8 @@ const password_reset_tokens = pgTable('password_reset_tokens', {
user_id: uuid('user_id') user_id: uuid('user_id')
.notNull() .notNull()
.references(() => users.id, { onDelete: 'cascade' }), .references(() => users.id, { onDelete: 'cascade' }),
expires_at: timestamp('expires_at', { mode: 'string' }), expires_at: timestamp('expires_at'),
created_at: timestamp('created_at', { mode: 'string' }).notNull().defaultNow(), created_at: timestamp('created_at').notNull().defaultNow(),
}); });
export type PasswordResetTokens = InferSelectModel<typeof password_reset_tokens>; export type PasswordResetTokens = InferSelectModel<typeof password_reset_tokens>;

View file

@ -11,8 +11,8 @@ const publishers = pgTable('publishers', {
.$defaultFn(() => cuid2()), .$defaultFn(() => cuid2()),
name: text('name'), name: text('name'),
slug: text('slug'), slug: text('slug'),
created_at: timestamp('created_at', { mode: 'string' }).notNull().defaultNow(), created_at: timestamp('created_at').notNull().defaultNow(),
updated_at: timestamp('updated_at', { mode: 'string' }).notNull().defaultNow(), updated_at: timestamp('updated_at').notNull().defaultNow(),
}); });
export type Publishers = InferSelectModel<typeof publishers>; export type Publishers = InferSelectModel<typeof publishers>;

View file

@ -9,8 +9,8 @@ const recovery_codes = pgTable('recovery_codes', {
.references(() => users.id), .references(() => users.id),
code: text('code').notNull(), code: text('code').notNull(),
used: boolean('used').default(false), used: boolean('used').default(false),
created_at: timestamp('created_at', { mode: 'string' }).notNull().defaultNow(), created_at: timestamp('created_at').notNull().defaultNow(),
updated_at: timestamp('updated_at', { mode: 'string' }).notNull().defaultNow(), updated_at: timestamp('updated_at').notNull().defaultNow(),
}); });
export type RecoveryCodes = InferSelectModel<typeof recovery_codes>; export type RecoveryCodes = InferSelectModel<typeof recovery_codes>;

View file

@ -10,8 +10,8 @@ const roles = pgTable('roles', {
.$defaultFn(() => cuid2()) .$defaultFn(() => cuid2())
.notNull(), .notNull(),
name: text('name').unique().notNull(), name: text('name').unique().notNull(),
created_at: timestamp('created_at', { mode: 'string' }).notNull().defaultNow(), created_at: timestamp('created_at').notNull().defaultNow(),
updated_at: timestamp('updated_at', { mode: 'string' }).notNull().defaultNow(), updated_at: timestamp('updated_at').notNull().defaultNow(),
}); });
export type Roles = InferSelectModel<typeof roles>; export type Roles = InferSelectModel<typeof roles>;

View file

@ -1,4 +1,5 @@
import { boolean, pgTable, text, timestamp, uuid } from 'drizzle-orm/pg-core'; import { boolean, pgTable, text, timestamp, uuid } from 'drizzle-orm/pg-core';
import { type InferSelectModel } from 'drizzle-orm';
import users from './users'; import users from './users';
const sessions = pgTable('sessions', { const sessions = pgTable('sessions', {
@ -12,7 +13,10 @@ const sessions = pgTable('sessions', {
}).notNull(), }).notNull(),
ipCountry: text('ip_country'), ipCountry: text('ip_country'),
ipAddress: text('ip_address'), ipAddress: text('ip_address'),
twoFactorAuthEnabled: boolean('two_factor_auth_enabled').default(false),
isTwoFactorAuthenticated: boolean('is_two_factor_authenticated').default(false), isTwoFactorAuthenticated: boolean('is_two_factor_authenticated').default(false),
}); });
export type Sessions = InferSelectModel<typeof sessions>;
export default sessions; export default sessions;

View file

@ -16,8 +16,8 @@ const user_roles = pgTable('user_roles', {
.notNull() .notNull()
.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', { mode: 'string' }).notNull().defaultNow(), created_at: timestamp('created_at').notNull().defaultNow(),
updated_at: timestamp('updated_at', { mode: 'string' }).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 }) => ({

View file

@ -18,8 +18,8 @@ const users = pgTable('users', {
theme: text('theme').default('system'), theme: text('theme').default('system'),
two_factor_secret: text('two_factor_secret').default(''), two_factor_secret: text('two_factor_secret').default(''),
two_factor_enabled: boolean('two_factor_enabled').default(false), two_factor_enabled: boolean('two_factor_enabled').default(false),
created_at: timestamp('created_at', { mode: 'string' }).notNull().defaultNow(), created_at: timestamp('created_at').notNull().defaultNow(),
updated_at: timestamp('updated_at', { mode: 'string' }).notNull().defaultNow(), updated_at: timestamp('updated_at').notNull().defaultNow(),
}); });
export const user_relations = relations(users, ({ many }) => ({ export const user_relations = relations(users, ({ many }) => ({

View file

@ -15,8 +15,8 @@ const wishlist_items = pgTable('wishlist_items', {
game_id: uuid('game_id') game_id: uuid('game_id')
.notNull() .notNull()
.references(() => games.id, { onDelete: 'cascade' }), .references(() => games.id, { onDelete: 'cascade' }),
created_at: timestamp('created_at', { mode: 'string' }).notNull().defaultNow(), created_at: timestamp('created_at').notNull().defaultNow(),
updated_at: timestamp('updated_at', { mode: 'string' }).notNull().defaultNow(), updated_at: timestamp('updated_at').notNull().defaultNow(),
}); });
export type WishlistItems = InferSelectModel<typeof wishlist_items>; export type WishlistItems = InferSelectModel<typeof wishlist_items>;

View file

@ -12,8 +12,8 @@ const wishlists = pgTable('wishlists', {
.notNull() .notNull()
.references(() => users.id, { onDelete: 'cascade' }), .references(() => users.id, { onDelete: 'cascade' }),
name: text('name').notNull().default('My Wishlist'), name: text('name').notNull().default('My Wishlist'),
created_at: timestamp('created_at', { mode: 'string' }).notNull().defaultNow(), created_at: timestamp('created_at').notNull().defaultNow(),
updated_at: timestamp('updated_at', { mode: 'string' }).notNull().defaultNow(), updated_at: timestamp('updated_at').notNull().defaultNow(),
}); });
export type Wishlists = InferSelectModel<typeof wishlists>; export type Wishlists = InferSelectModel<typeof wishlists>;

View file

@ -1,20 +1,50 @@
import 'dotenv/config'; import { Table, getTableName, sql } from 'drizzle-orm';
import { drizzle } from 'drizzle-orm/node-postgres';
import pg from 'pg';
import * as schema from './schema';
import { Argon2id } from 'oslo/password'; import { Argon2id } from 'oslo/password';
import { ADMIN_USERNAME, DB_SEEDING } from '$env/static/private';
import { db } from '../db';
import * as schema from './schema';
import * as seeds from './seeds';
// create the connection if (!DB_SEEDING) {
const pool = new pg.Pool({ throw new Error('You must set DB_SEEDING to "true" when running seeds');
user: process.env.DATABASE_USER, }
password: process.env.DATABASE_PASSWORD,
host: process.env.DATABASE_HOST,
port: Number(process.env.DATABASE_PORT).valueOf(),
database: process.env.DATABASE_DB,
ssl: process.env.DATABASE_HOST !== 'localhost',
});
const db = drizzle(pool, { schema: schema }); async function resetTable(db: db, table: Table) {
return db.execute(sql.raw(`TRUNCATE TABLE ${getTableName(table)} RESTART IDENTITY CASCADE`));
}
for (const table of [
schema.categories,
schema.categoriesToExternalIds,
schema.categories_to_games,
schema.collection_items,
schema.collections,
schema.expansions,
schema.externalIds,
schema.games,
schema.gamesToExternalIds,
schema.mechanics,
schema.mechanicsToExternalIds,
schema.mechanics_to_games,
schema.password_reset_tokens,
schema.publishers,
schema.publishersToExternalIds,
schema.publishers_to_games,
schema.recoveryCodes,
schema.roles,
schema.sessions,
schema.userRoles,
schema.users,
schema.wishlists,
schema.wishlist_items,
]) {
// await db.delete(table); // clear tables without truncating / resetting ids
await resetTable(db, table);
}
await seeds.roles(db);
await connection.end();
console.log('Creating roles ...'); console.log('Creating roles ...');
const adminRole = await db const adminRole = await db
@ -42,7 +72,7 @@ console.log('Admin Role: ', adminRole);
const adminUser = await db const adminUser = await db
.insert(schema.users) .insert(schema.users)
.values({ .values({
username: `${process.env.ADMIN_USERNAME}`, username: `${ADMIN_USERNAME}`,
email: '', email: '',
hashed_password: await new Argon2id().hash(`${process.env.ADMIN_PASSWORD}`), hashed_password: await new Argon2id().hash(`${process.env.ADMIN_PASSWORD}`),
first_name: 'Brad', first_name: 'Brad',

View file

View file

@ -0,0 +1,23 @@
[
{
"first_name": "John",
"last_name": "Smith",
"username": "john.smith",
"email": "john.smith@example.com",
"password": "password"
},
{
"first_name": "Jane",
"last_name": "Doe",
"username": "jane.doe",
"email": "jane.doe@example.com",
"password": "password"
},
{
"first_name": "Michael",
"last_name": "Jones",
"username": "michael.jones",
"email": "michael.jones@example.com",
"password": "password"
}
]

30
src/db/seeds/roles.ts Normal file
View file

@ -0,0 +1,30 @@
import { eq } from 'drizzle-orm';
import type db from '@/db';
import * as schema from '@/db/schema';
import roles from './data/roles.json';
// console.log('Creating roles ...');
// const adminRole = await db
// .insert(schema.roles)
// .values([{ name: 'admin' }])
// .onConflictDoNothing()
// .returning();
// const userRole = await db
// .insert(schema.roles)
// .values([{ name: 'user' }])
// .onConflictDoNothing()
// .returning();
// await db
// .insert(schema.roles)
// .values([{ name: 'editor' }])
// .onConflictDoNothing();
// await db
// .insert(schema.roles)
// .values([{ name: 'moderator' }])
// .onConflictDoNothing();
// console.log('Roles created.');
export default async function seed(db: db) {
await db.insert(schema.roles).values({ name: 'user' });
await db.insert(schema.roles).values({ name: 'admin' });
}

41
src/db/seeds/users.ts Normal file
View file

@ -0,0 +1,41 @@
import { eq } from 'drizzle-orm';
import * as argon2 from 'argon2';
import type db from '@/db';
import * as schema from '@/db/schema';
import users from './data/users.json';
async function getCityId(db: db, cityName: string) {
const city = await db.query.city.findFirst({
where: eq(schema.city.name, cityName),
});
if (!city) {
throw new Error('Unknown city name: ' + cityName);
}
return city.id;
}
export default async function seed(db: db) {
await Promise.all(
users.map(async (user) => {
const [insertedUser] = await db
.insert(schema.user)
.values({
...user,
emailVerified: true,
phoneVerified: true,
password: await argon2.hash(user.password),
})
.returning();
await Promise.all(
user.addresses.map(async (address) => {
await db.insert(schema.address).values({
...address,
streetAddress1: address.street_address,
userId: insertedUser.id,
cityId: await getCityId(db, address.city),
});
}),
);
}),
);
}

View file

@ -1,4 +1,4 @@
import { generateIdFromEntropySize } from 'lucia'; import { generateIdFromEntropySize, type Session, type User } from 'lucia';
import { TimeSpan, createDate } from 'oslo'; import { TimeSpan, createDate } from 'oslo';
import { eq } from 'drizzle-orm'; import { eq } from 'drizzle-orm';
import db from '../../db'; import db from '../../db';
@ -15,3 +15,14 @@ export async function createPasswordResetToken(userId: string): Promise<string>
}); });
return tokenId; return tokenId;
} }
/**
* Checks if the user is fully authenticated.
*
* @param user - The user object.
* @param session - The session object.
* @returns True if the user is fully authenticated, otherwise false.
*/
export function userFullyAuthenticated(user: User | null, session: Session | null) {
return user && session && (!session.isTwoFactorAuthEnabled || session.isTwoFactorAuthenticated);
}

View file

@ -20,6 +20,7 @@ export const lucia = new Lucia(adapter, {
return { return {
ipCountry: attributes.ip_country, ipCountry: attributes.ip_country,
ipAddress: attributes.ip_address, ipAddress: attributes.ip_address,
isTwoFactorAuthEnabled: attributes.twoFactorAuthEnabled,
isTwoFactorAuthenticated: attributes.isTwoFactorAuthenticated, isTwoFactorAuthenticated: attributes.isTwoFactorAuthenticated,
}; };
}, },
@ -54,6 +55,7 @@ declare module 'lucia' {
interface DatabaseSessionAttributes { interface DatabaseSessionAttributes {
ip_country: string; ip_country: string;
ip_address: string; ip_address: string;
twoFactorAuthEnabled: boolean;
isTwoFactorAuthenticated: boolean; isTwoFactorAuthenticated: boolean;
} }
interface DatabaseUserAttributes { interface DatabaseUserAttributes {

View file

@ -3,14 +3,15 @@ import { forbiddenMessage, notSignedInMessage } from '$lib/flashMessages';
import { eq } from 'drizzle-orm'; import { eq } from 'drizzle-orm';
import db from '../../../../db'; import db from '../../../../db';
import { user_roles } from '$db/schema'; import { user_roles } from '$db/schema';
import { userFullyAuthenticated } from '$lib/server/auth-utils';
export const load = loadFlash(async (event) => { export const load = loadFlash(async (event) => {
const { locals } = event; const { locals } = event;
if (!locals?.user) { const { user, session } = locals;
if (userFullyAuthenticated(user, session)) {
redirect(302, '/login', notSignedInMessage, event); redirect(302, '/login', notSignedInMessage, event);
} }
const { user } = locals;
const userRoles = await db.query.user_roles.findMany({ const userRoles = await db.query.user_roles.findMany({
where: eq(user_roles.user_id, user.id), where: eq(user_roles.user_id, user.id),
with: { with: {

View file

@ -2,11 +2,13 @@ import { redirect } from 'sveltekit-flash-message/server';
import type { PageServerLoad } from './$types'; import type { PageServerLoad } from './$types';
import { notSignedInMessage } from '$lib/flashMessages'; import { notSignedInMessage } from '$lib/flashMessages';
import db from '../../../../../db'; import db from '../../../../../db';
import { userFullyAuthenticated } from '$lib/server/auth-utils';
export const load: PageServerLoad = async (event) => { export const load: PageServerLoad = async (event) => {
// TODO: Ensure admin user const { locals } = event;
if (!event.locals.user) { const { user, session } = locals;
redirect(302, '/login', notSignedInMessage, event); if (userFullyAuthenticated(user, session)) {
return fail(401);
} }
const users = await db.query.users.findMany({ const users = await db.query.users.findMany({

View file

@ -8,10 +8,11 @@ import { search_schema } from '$lib/zodValidation.js';
import db from '../../../../db'; import db from '../../../../db';
import { collection_items, collections, games } from '$db/schema'; import { collection_items, collections, games } from '$db/schema';
import { notSignedInMessage } from '$lib/flashMessages'; import { notSignedInMessage } from '$lib/flashMessages';
import { userFullyAuthenticated } from '$lib/server/auth-utils';
export async function load(event) { export async function load(event) {
const user = event.locals.user; const { user, session } = event.locals;
if (!user) { if (userFullyAuthenticated(user, session)) {
redirect(302, '/login', notSignedInMessage, event); redirect(302, '/login', notSignedInMessage, event);
} }

View file

@ -8,16 +8,16 @@ import db from '../../../../../db';
import { notSignedInMessage } from '$lib/flashMessages.js'; import { notSignedInMessage } from '$lib/flashMessages.js';
import { collections, games, collection_items } from '$db/schema'; import { collections, games, collection_items } from '$db/schema';
import { search_schema } from '$lib/zodValidation'; import { search_schema } from '$lib/zodValidation';
import type { UICollection } from '$lib/types'; import { userFullyAuthenticated } from '$lib/server/auth-utils';
export async function load(event) { export async function load(event) {
const { locals, params, url } = event; const { locals, params, url } = event;
const { user } = locals; const { user, session } = locals;
const { id } = params; const { id } = params;
if (!user) {
if (userFullyAuthenticated(user, session)) {
redirect(302, '/login', notSignedInMessage, event); redirect(302, '/login', notSignedInMessage, event);
} }
const searchParams = Object.fromEntries(url?.searchParams); const searchParams = Object.fromEntries(url?.searchParams);
console.log('searchParams', searchParams); console.log('searchParams', searchParams);
const q = searchParams?.q; const q = searchParams?.q;
@ -102,12 +102,14 @@ export async function load(event) {
export const actions: Actions = { export const actions: Actions = {
// Add game to a wishlist // Add game to a wishlist
add: async (event) => { add: async (event) => {
const form = await superValidate(event, zod(modifyListGameSchema)); const { locals } = event;
const { user, session } = locals;
if (!event.locals.user) { if (userFullyAuthenticated(user, session)) {
return fail(401, {}); return fail(401);
} }
const form = await superValidate(event, zod(modifyListGameSchema));
const user = event.locals.user; const user = event.locals.user;
const game = await db.query.games.findFirst({ const game = await db.query.games.findFirst({
where: eq(games.id, form.data.id), where: eq(games.id, form.data.id),
@ -148,15 +150,19 @@ export const actions: Actions = {
} }
}, },
// Create new wishlist // Create new wishlist
create: async ({ locals }) => { create: async (event) => {
if (!locals.user) { const { locals } = event;
const { user, session } = locals;
if (userFullyAuthenticated(user, session)) {
return fail(401); return fail(401);
} }
return error(405, 'Method not allowed'); return error(405, 'Method not allowed');
}, },
// Delete a wishlist // Delete a wishlist
delete: async ({ locals }) => { delete: async (event) => {
if (!locals.user) { const { locals } = event;
const { user, session } = locals;
if (userFullyAuthenticated(user, session)) {
return fail(401); return fail(401);
} }
return error(405, 'Method not allowed'); return error(405, 'Method not allowed');
@ -164,11 +170,11 @@ export const actions: Actions = {
// Remove game from a wishlist // Remove game from a wishlist
remove: async (event) => { remove: async (event) => {
const { locals } = event; const { locals } = event;
const form = await superValidate(event, zod(modifyListGameSchema)); const { user, session } = locals;
if (userFullyAuthenticated(user, session)) {
if (!locals.user) {
return fail(401); return fail(401);
} }
const form = await superValidate(event, zod(modifyListGameSchema));
const game = await db.query.games.findFirst({ const game = await db.query.games.findFirst({
where: eq(games.id, form.data.id), where: eq(games.id, form.data.id),

View file

@ -1,13 +1,14 @@
import { redirect } from "@sveltejs/kit"; import { redirect } from '@sveltejs/kit';
import type { PageServerLoad } from "../$types"; import type { PageServerLoad } from '../$types';
import { notSignedInMessage } from "$lib/flashMessages"; import { notSignedInMessage } from '$lib/flashMessages';
import { userFullyAuthenticated } from '$lib/server/auth-utils';
export async function load(event) { export async function load(event) {
const { locals } = event; const { locals } = event;
const user = locals.user; const { user, session } = locals;
if (!user) { if (userFullyAuthenticated(user, session)) {
redirect(302, '/login', notSignedInMessage, event); redirect(302, '/login', notSignedInMessage, event);
} }
return {} return {};
} }

View file

@ -1,16 +1,19 @@
import { redirect } from "@sveltejs/kit"; import { redirect } from '@sveltejs/kit';
import { superValidate } from "sveltekit-superforms/server"; import { superValidate } from 'sveltekit-superforms/server';
import { zod } from 'sveltekit-superforms/adapters'; import { zod } from 'sveltekit-superforms/adapters';
import type { PageServerLoad } from "../$types"; import type { PageServerLoad } from '../$types';
import { BggForm } from "$lib/zodValidation"; import { BggForm } from '$lib/zodValidation';
import { userFullyAuthenticated } from '$lib/server/auth-utils';
import { notSignedInMessage } from '$lib/flashMessages';
export const load: PageServerLoad = async ({ locals, fetch }) => { export const load: PageServerLoad = async (event) => {
const user = locals.user; const { locals } = event;
if (!user) { const { user, session } = locals;
redirect(302, '/login'); if (userFullyAuthenticated(user, session)) {
redirect(302, '/login', notSignedInMessage, event);
} }
const form = await superValidate({}, zod(BggForm)); const form = await superValidate({}, zod(BggForm));
return { form }; return { form };
} };

View file

@ -2,9 +2,12 @@ import { fail } from '@sveltejs/kit';
import { eq } from 'drizzle-orm'; import { eq } from 'drizzle-orm';
import db from '../../../../db'; import db from '../../../../db';
import { wishlists } from '$db/schema'; import { wishlists } from '$db/schema';
import { userFullyAuthenticated } from '$lib/server/auth-utils';
import { notSignedInMessage } from '$lib/flashMessages';
export async function load({ locals }) { export async function load({ locals }) {
if (!locals.user) { const { user, session } = locals;
if (userFullyAuthenticated(user, session)) {
throw fail(401); throw fail(401);
} }

View file

@ -5,11 +5,14 @@ import { superValidate } from 'sveltekit-superforms/server';
import db from '../../../../../db'; import db from '../../../../../db';
import { modifyListGameSchema } from '$lib/validations/zod-schemas'; import { modifyListGameSchema } from '$lib/validations/zod-schemas';
import { games, wishlist_items, wishlists } from '$db/schema'; import { games, wishlist_items, wishlists } from '$db/schema';
import { userFullyAuthenticated } from '$lib/server/auth-utils';
import { notSignedInMessage } from '$lib/flashMessages';
export async function load({ params, locals }) { export async function load(event) {
const user = locals.user; const { params, locals } = event;
if (!user) { const { user, session } = locals;
redirect(302, '/login'); if (userFullyAuthenticated(user, session)) {
redirect(302, '/login', notSignedInMessage, event);
} }
try { try {
@ -40,6 +43,10 @@ export const actions: Actions = {
// Add game to a wishlist // Add game to a wishlist
add: async (event) => { add: async (event) => {
const { params, locals } = event; const { params, locals } = event;
const { user, session } = locals;
if (userFullyAuthenticated(user, session)) {
return fail(401);
}
const form = await superValidate(event, zod(modifyListGameSchema)); const form = await superValidate(event, zod(modifyListGameSchema));
if (!locals.user) { if (!locals.user) {
@ -92,21 +99,27 @@ export const actions: Actions = {
}; };
}, },
// Create new wishlist // Create new wishlist
create: async ({ locals }) => { create: async (event) => {
if (!locals.user) { const { locals } = event;
throw fail(401); const { user, session } = locals;
if (userFullyAuthenticated(user, session)) {
return fail(401);
} }
}, },
// Delete a wishlist // Delete a wishlist
delete: async ({ locals }) => { delete: async (event) => {
if (!locals.user) { const { locals } = event;
throw fail(401); const { user, session } = locals;
if (userFullyAuthenticated(user, session)) {
return fail(401);
} }
}, },
// Remove game from a wishlist // Remove game from a wishlist
remove: async ({ locals }) => { remove: async (event) => {
if (!locals.user) { const { locals } = event;
throw fail(401); const { user, session } = locals;
if (userFullyAuthenticated(user, session)) {
return fail(401);
} }
}, },
}; };

View file

@ -9,9 +9,12 @@ import { notSignedInMessage } from '$lib/flashMessages';
import db from '../../../../db'; import db from '../../../../db';
import type { PageServerLoad } from './$types'; import type { PageServerLoad } from './$types';
import { users } from '$db/schema'; import { users } from '$db/schema';
import { userFullyAuthenticated } from '$lib/server/auth-utils';
export const load: PageServerLoad = async (event) => { export const load: PageServerLoad = async (event) => {
if (!event.locals.user) { const { locals } = event;
const { user, session } = locals;
if (userFullyAuthenticated(user, session)) {
redirect(302, '/login', notSignedInMessage, event); redirect(302, '/login', notSignedInMessage, event);
} }

View file

@ -11,12 +11,13 @@ import { lucia } from '$lib/server/auth.js';
import { users } from '$db/schema'; import { users } from '$db/schema';
import { notSignedInMessage } from '$lib/flashMessages'; import { notSignedInMessage } from '$lib/flashMessages';
import type { Cookie } from 'lucia'; import type { Cookie } from 'lucia';
import { userFullyAuthenticated } from '$lib/server/auth-utils';
export const load: PageServerLoad = async (event) => { export const load: PageServerLoad = async (event) => {
const form = await superValidate(event, zod(changeUserPasswordSchema)); const form = await superValidate(event, zod(changeUserPasswordSchema));
const user = event.locals.user; const { locals } = event;
const { user, session } = locals;
if (!user) { if (userFullyAuthenticated(user, session)) {
redirect(302, '/login', notSignedInMessage, event); redirect(302, '/login', notSignedInMessage, event);
} }
@ -32,6 +33,12 @@ export const load: PageServerLoad = async (event) => {
export const actions: Actions = { export const actions: Actions = {
default: async (event) => { default: async (event) => {
const { locals } = event;
const { user, session } = locals;
if (userFullyAuthenticated(user, session)) {
return fail(401);
}
const form = await superValidate(event, zod(changeUserPasswordSchema)); const form = await superValidate(event, zod(changeUserPasswordSchema));
if (!form.valid) { if (!form.valid) {

View file

@ -13,13 +13,15 @@ import { addTwoFactorSchema, removeTwoFactorSchema } from '$lib/validations/acco
import { notSignedInMessage } from '$lib/flashMessages'; import { notSignedInMessage } from '$lib/flashMessages';
import db from '../../../../../../db'; import db from '../../../../../../db';
import { recovery_codes, users } from '$db/schema'; import { recovery_codes, users } from '$db/schema';
import { userFullyAuthenticated } from '$lib/server/auth-utils';
export const load: PageServerLoad = async (event) => { export const load: PageServerLoad = async (event) => {
const addTwoFactorForm = await superValidate(event, zod(addTwoFactorSchema)); const addTwoFactorForm = await superValidate(event, zod(addTwoFactorSchema));
const removeTwoFactorForm = await superValidate(event, zod(removeTwoFactorSchema)); const removeTwoFactorForm = await superValidate(event, zod(removeTwoFactorSchema));
const user = event.locals.user;
if (!user) { const { locals } = event;
const { user, session } = locals;
if (userFullyAuthenticated(user, session)) {
redirect(302, '/login', notSignedInMessage, event); redirect(302, '/login', notSignedInMessage, event);
} }
@ -67,6 +69,12 @@ export const load: PageServerLoad = async (event) => {
export const actions: Actions = { export const actions: Actions = {
enableTwoFactor: async (event) => { enableTwoFactor: async (event) => {
const { params, locals } = event;
const { user, session } = locals;
if (userFullyAuthenticated(user, session)) {
return fail(401);
}
const addTwoFactorForm = await superValidate(event, zod(addTwoFactorSchema)); const addTwoFactorForm = await superValidate(event, zod(addTwoFactorSchema));
if (!addTwoFactorForm.valid) { if (!addTwoFactorForm.valid) {

View file

@ -6,11 +6,12 @@ import { redirect } from 'sveltekit-flash-message/server';
import { notSignedInMessage } from '$lib/flashMessages'; import { notSignedInMessage } from '$lib/flashMessages';
import type { PageServerLoad } from '../../../$types'; import type { PageServerLoad } from '../../../$types';
import { recovery_codes, users } from '$db/schema'; import { recovery_codes, users } from '$db/schema';
import { userFullyAuthenticated } from '$lib/server/auth-utils';
export const load: PageServerLoad = async (event) => { export const load: PageServerLoad = async (event) => {
const user = event.locals.user; const { locals } = event;
const { user, session } = locals;
if (!user) { if (userFullyAuthenticated(user, session)) {
redirect(302, '/login', notSignedInMessage, event); redirect(302, '/login', notSignedInMessage, event);
} }

View file

@ -7,10 +7,12 @@ import { modifyListGameSchema } from '$lib/validations/zod-schemas';
import db from '../../../../db'; import db from '../../../../db';
import { notSignedInMessage } from '$lib/flashMessages.js'; import { notSignedInMessage } from '$lib/flashMessages.js';
import { games, wishlist_items, wishlists } from '$db/schema'; import { games, wishlist_items, wishlists } from '$db/schema';
import { userFullyAuthenticated } from '$lib/server/auth-utils';
export async function load(event) { export async function load(event) {
const user = event.locals.user; const { params, locals } = event;
if (!user) { const { user, session } = locals;
if (userFullyAuthenticated(user, session)) {
redirect(302, '/login', notSignedInMessage, event); redirect(302, '/login', notSignedInMessage, event);
} }
@ -38,13 +40,13 @@ export const actions: Actions = {
// Add game to a wishlist // Add game to a wishlist
add: async (event) => { add: async (event) => {
const { locals } = event; const { locals } = event;
const { user, session } = locals;
if (userFullyAuthenticated(user, session)) {
return fail(401);
}
const form = await superValidate(event, zod(modifyListGameSchema)); const form = await superValidate(event, zod(modifyListGameSchema));
try { try {
if (!locals.user) {
redirect(302, '/login', notSignedInMessage, event);
}
const game = await db.query.games.findFirst({ const game = await db.query.games.findFirst({
where: eq(games.id, form.data.id), where: eq(games.id, form.data.id),
}); });
@ -86,28 +88,31 @@ export const actions: Actions = {
// Create new wishlist // Create new wishlist
create: async (event) => { create: async (event) => {
const { locals } = event; const { locals } = event;
if (!locals.user) { const { user, session } = locals;
redirect(302, '/login', notSignedInMessage, event); if (userFullyAuthenticated(user, session)) {
return fail(401);
} }
return error(405, 'Method not allowed'); return error(405, 'Method not allowed');
}, },
// Delete a wishlist // Delete a wishlist
delete: async ({ locals }) => { delete: async (event) => {
if (!locals.user) { const { locals } = event;
redirect(302, '/login'); const { user, session } = locals;
if (userFullyAuthenticated(user, session)) {
return fail(401);
} }
return error(405, 'Method not allowed'); return error(405, 'Method not allowed');
}, },
// Remove game from a wishlist // Remove game from a wishlist
remove: async (event) => { remove: async (event) => {
const { locals } = event; const { params, locals } = event;
const { user, session } = locals;
if (userFullyAuthenticated(user, session)) {
return fail(401);
}
const form = await superValidate(event, zod(modifyListGameSchema)); const form = await superValidate(event, zod(modifyListGameSchema));
try { try {
if (!locals.user) {
redirect(302, '/login', notSignedInMessage, event);
}
const game = await db.query.games.findFirst({ const game = await db.query.games.findFirst({
where: eq(games.id, form.data.id), where: eq(games.id, form.data.id),
}); });

View file

@ -7,11 +7,13 @@ import { modifyListGameSchema } from '$lib/validations/zod-schemas';
import db from '../../../../../db'; import db from '../../../../../db';
import { notSignedInMessage } from '$lib/flashMessages.js'; import { notSignedInMessage } from '$lib/flashMessages.js';
import { games, wishlist_items, wishlists } from '$db/schema'; import { games, wishlist_items, wishlists } from '$db/schema';
import { userFullyAuthenticated } from '$lib/server/auth-utils';
export async function load(event) { export async function load(event) {
const { params, locals } = event; const { params, locals } = event;
const { user, session } = locals;
const { id } = params; const { id } = params;
if (!locals.user) { if (userFullyAuthenticated(user, session)) {
redirect(302, '/login', notSignedInMessage, event); redirect(302, '/login', notSignedInMessage, event);
} }
@ -41,13 +43,13 @@ export const actions: Actions = {
// Add game to a wishlist // Add game to a wishlist
add: async (event) => { add: async (event) => {
const { locals } = event; const { locals } = event;
const { user, session } = locals;
if (userFullyAuthenticated(user, session)) {
return fail(401);
}
const form = await superValidate(event, zod(modifyListGameSchema)); const form = await superValidate(event, zod(modifyListGameSchema));
try { try {
if (!locals.user) {
redirect(302, '/login', notSignedInMessage, event);
}
const game = await db.query.games.findFirst({ const game = await db.query.games.findFirst({
where: eq(games.id, form.data.id), where: eq(games.id, form.data.id),
}); });
@ -89,28 +91,31 @@ export const actions: Actions = {
// Create new wishlist // Create new wishlist
create: async (event) => { create: async (event) => {
const { locals } = event; const { locals } = event;
if (!locals.user) { const { user, session } = locals;
redirect(302, '/login', notSignedInMessage, event); if (userFullyAuthenticated(user, session)) {
return fail(401);
} }
return error(405, 'Method not allowed'); return error(405, 'Method not allowed');
}, },
// Delete a wishlist // Delete a wishlist
delete: async ({ locals }) => { delete: async ({ locals }) => {
if (!locals.user) { const { locals } = event;
redirect(302, '/login'); const { user, session } = locals;
if (userFullyAuthenticated(user, session)) {
return fail(401);
} }
return error(405, 'Method not allowed'); return error(405, 'Method not allowed');
}, },
// Remove game from a wishlist // Remove game from a wishlist
remove: async (event) => { remove: async (event) => {
const { locals } = event; const { locals } = event;
const { user, session } = locals;
if (userFullyAuthenticated(user, session)) {
return fail(401);
}
const form = await superValidate(event, zod(modifyListGameSchema)); const form = await superValidate(event, zod(modifyListGameSchema));
try { try {
if (!locals.user) {
redirect(302, '/login', notSignedInMessage, event);
}
const game = await db.query.games.findFirst({ const game = await db.query.games.findFirst({
where: eq(games.id, form.data.id), where: eq(games.id, form.data.id),
}); });

View file

@ -10,7 +10,7 @@ import { RateLimiter } from 'sveltekit-rate-limiter/server';
import db from '../../../db'; import db from '../../../db';
import { lucia } from '$lib/server/auth'; import { lucia } from '$lib/server/auth';
import { signInSchema } from '$lib/validations/auth'; import { signInSchema } from '$lib/validations/auth';
import { users, recovery_codes } from '$db/schema'; import { users, recoveryCodes, type Users } from '$db/schema';
import type { PageServerLoad } from './$types'; import type { PageServerLoad } from './$types';
export const load: PageServerLoad = async (event) => { export const load: PageServerLoad = async (event) => {
@ -49,10 +49,14 @@ export const actions: Actions = {
let session; let session;
let sessionCookie; let sessionCookie;
const user = await db.query.users.findFirst({ const user: Users | undefined = await db.query.users.findFirst({
where: eq(users.username, form.data.username), where: eq(users.username, form.data.username),
}); });
if (!user) {
form.data.password = '';
return setError(form, 'username', 'Your username or password is incorrect.');
}
try { try {
const password = form.data.password; const password = form.data.password;
@ -76,6 +80,10 @@ export const actions: Actions = {
session = await lucia.createSession(user.id, { session = await lucia.createSession(user.id, {
ip_country: locals.country, ip_country: locals.country,
ip_address: locals.ip, ip_address: locals.ip,
twoFactorAuthEnabled:
user?.two_factor_enabled &&
user?.two_factor_secret !== null &&
user?.two_factor_secret !== '',
isTwoFactorAuthenticated: false, isTwoFactorAuthenticated: false,
}); });
console.log('logging in session', session); console.log('logging in session', session);
@ -97,7 +105,11 @@ export const actions: Actions = {
form.data.username = ''; form.data.username = '';
form.data.password = ''; form.data.password = '';
if (user?.two_factor_enabled && user?.two_factor_secret) { if (
user?.two_factor_enabled &&
user?.two_factor_secret !== null &&
user?.two_factor_secret !== ''
) {
console.log('redirecting to TOTP page'); console.log('redirecting to TOTP page');
const message = { type: 'success', message: 'Please enter your TOTP code.' } as const; const message = { type: 'success', message: 'Please enter your TOTP code.' } as const;
redirect(302, '/totp', message, event); redirect(302, '/totp', message, event);

View file

@ -12,21 +12,30 @@ import { lucia } from '$lib/server/auth';
import { totpSchema } from '$lib/validations/auth'; import { totpSchema } from '$lib/validations/auth';
import { users, recovery_codes } from '$db/schema'; import { users, recovery_codes } from '$db/schema';
import type { PageServerLoad } from './$types'; import type { PageServerLoad } from './$types';
import { notSignedInMessage } from '$lib/flashMessages';
export const load: PageServerLoad = async (event) => { export const load: PageServerLoad = async (event) => {
if (event.locals.user) { const { user, session } = event.locals;
const user = event.locals.user;
if (!user || !session) {
redirect(302, '/login', notSignedInMessage, event);
}
if (user && session) {
const dbUser = await db.query.users.findFirst({ const dbUser = await db.query.users.findFirst({
where: eq(users.username, user.username), where: eq(users.username, user.username),
}); });
const session = event.locals.session;
const isTwoFactorAuthenticated = session?.isTwoFactorAuthenticated; const isTwoFactorAuthenticated = session?.isTwoFactorAuthenticated;
console.log('session', session); console.log('session', session);
console.log('isTwoFactorAuthenticated', isTwoFactorAuthenticated); console.log('isTwoFactorAuthenticated', isTwoFactorAuthenticated);
if (isTwoFactorAuthenticated && dbUser?.two_factor_enabled && dbUser?.two_factor_secret) { if (
isTwoFactorAuthenticated &&
dbUser?.two_factor_enabled &&
dbUser?.two_factor_secret !== ''
) {
const message = { type: 'success', message: 'You are already signed in' } as const; const message = { type: 'success', message: 'You are already signed in' } as const;
throw redirect('/', message, event); throw redirect('/', message, event);
} }
@ -68,7 +77,11 @@ export const actions: Actions = {
const isTwoFactorAuthenticated = session?.isTwoFactorAuthenticated; const isTwoFactorAuthenticated = session?.isTwoFactorAuthenticated;
if (isTwoFactorAuthenticated && dbUser?.two_factor_enabled && dbUser?.two_factor_secret) { if (
isTwoFactorAuthenticated &&
dbUser?.two_factor_enabled &&
dbUser?.two_factor_secret !== ''
) {
const message = { type: 'success', message: 'You are already signed in' } as const; const message = { type: 'success', message: 'You are already signed in' } as const;
throw redirect('/', message, event); throw redirect('/', message, event);
} }
@ -86,12 +99,12 @@ export const actions: Actions = {
try { try {
const totpToken = form?.data?.totpToken; const totpToken = form?.data?.totpToken;
if (dbUser?.two_factor_enabled && dbUser?.two_factor_secret && !totpToken) { if (dbUser?.two_factor_enabled && dbUser?.two_factor_secret !== '' && !totpToken) {
return fail(400, { return fail(400, {
form, form,
}); });
} else if (dbUser?.two_factor_enabled && dbUser?.two_factor_secret && totpToken) { } else if (dbUser?.two_factor_enabled && dbUser?.two_factor_secret !== '' && totpToken) {
console.log('totpToken',totpToken); console.log('totpToken', totpToken);
const validOTP = await new TOTPController().verify( const validOTP = await new TOTPController().verify(
totpToken, totpToken,
decodeHex(dbUser.two_factor_secret), decodeHex(dbUser.two_factor_secret),
@ -113,6 +126,7 @@ export const actions: Actions = {
const newSession = await lucia.createSession(dbUser.id, { const newSession = await lucia.createSession(dbUser.id, {
ip_country: locals.country, ip_country: locals.country,
ip_address: locals.ip, ip_address: locals.ip,
twoFactorAuthEnabled: true,
isTwoFactorAuthenticated: true, isTwoFactorAuthenticated: true,
}); });
console.log('logging in session', newSession); console.log('logging in session', newSession);