diff --git a/package.json b/package.json index c00a9a0..dd9738d 100644 --- a/package.json +++ b/package.json @@ -29,12 +29,12 @@ "@melt-ui/svelte": "^0.83.0", "@playwright/test": "^1.46.1", "@sveltejs/adapter-auto": "^3.2.4", - "@sveltejs/enhanced-img": "^0.3.3", - "@sveltejs/kit": "^2.5.24", + "@sveltejs/enhanced-img": "^0.3.4", + "@sveltejs/kit": "^2.5.25", "@sveltejs/vite-plugin-svelte": "^3.1.2", "@types/cookie": "^0.6.0", - "@types/node": "^20.16.1", - "@types/pg": "^8.11.6", + "@types/node": "^20.16.2", + "@types/pg": "^8.11.8", "@typescript-eslint/eslint-plugin": "^7.18.0", "@typescript-eslint/parser": "^7.18.0", "autoprefixer": "^10.4.20", @@ -95,7 +95,7 @@ "arctic": "^1.9.2", "bits-ui": "^0.21.13", "boardgamegeekclient": "^1.9.1", - "bullmq": "^5.12.10", + "bullmq": "^5.12.12", "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", "cookie": "^0.6.0", @@ -114,7 +114,7 @@ "just-capitalize": "^3.2.0", "just-kebab-case": "^4.2.0", "loader": "^2.1.1", - "open-props": "^1.7.5", + "open-props": "^1.7.6", "oslo": "^1.2.1", "pg": "^8.12.0", "postgres": "^3.4.4", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 88491f8..fd902d0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -31,7 +31,7 @@ importers: version: 3.5.5 '@lucia-auth/adapter-drizzle': specifier: ^1.1.0 - version: 1.1.0(drizzle-orm@0.32.2(@neondatabase/serverless@0.9.4)(@types/pg@8.11.6)(pg@8.12.0)(postgres@3.4.4))(lucia@3.2.0) + version: 1.1.0(drizzle-orm@0.32.2(@neondatabase/serverless@0.9.4)(@types/pg@8.11.8)(pg@8.12.0)(postgres@3.4.4))(lucia@3.2.0) '@lukeed/uuid': specifier: ^2.0.1 version: 2.0.1 @@ -46,10 +46,10 @@ importers: version: 2.6.2 '@sveltejs/adapter-node': specifier: ^5.2.2 - version: 5.2.2(@sveltejs/kit@2.5.24(@sveltejs/vite-plugin-svelte@3.1.2(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.1)(sass@1.77.8)))(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.1)(sass@1.77.8))) + version: 5.2.2(@sveltejs/kit@2.5.25(@sveltejs/vite-plugin-svelte@3.1.2(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.2)(sass@1.77.8)))(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.2)(sass@1.77.8))) '@sveltejs/adapter-vercel': specifier: ^5.4.3 - version: 5.4.3(@sveltejs/kit@2.5.24(@sveltejs/vite-plugin-svelte@3.1.2(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.1)(sass@1.77.8)))(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.1)(sass@1.77.8))) + version: 5.4.3(@sveltejs/kit@2.5.25(@sveltejs/vite-plugin-svelte@3.1.2(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.2)(sass@1.77.8)))(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.2)(sass@1.77.8))) '@types/feather-icons': specifier: ^4.29.4 version: 4.29.4 @@ -66,8 +66,8 @@ importers: specifier: ^1.9.1 version: 1.9.1 bullmq: - specifier: ^5.12.10 - version: 5.12.10 + specifier: ^5.12.12 + version: 5.12.12 class-variance-authority: specifier: ^0.7.0 version: 0.7.0 @@ -85,16 +85,16 @@ importers: version: 11.0.6 drizzle-orm: specifier: ^0.32.2 - version: 0.32.2(@neondatabase/serverless@0.9.4)(@types/pg@8.11.6)(pg@8.12.0)(postgres@3.4.4) + version: 0.32.2(@neondatabase/serverless@0.9.4)(@types/pg@8.11.8)(pg@8.12.0)(postgres@3.4.4) drizzle-zod: specifier: ^0.5.1 - version: 0.5.1(drizzle-orm@0.32.2(@neondatabase/serverless@0.9.4)(@types/pg@8.11.6)(pg@8.12.0)(postgres@3.4.4))(zod@3.23.8) + version: 0.5.1(drizzle-orm@0.32.2(@neondatabase/serverless@0.9.4)(@types/pg@8.11.8)(pg@8.12.0)(postgres@3.4.4))(zod@3.23.8) feather-icons: specifier: ^4.29.2 version: 4.29.2 formsnap: specifier: ^1.0.1 - version: 1.0.1(svelte@5.0.0-next.175)(sveltekit-superforms@2.17.0(@sveltejs/kit@2.5.24(@sveltejs/vite-plugin-svelte@3.1.2(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.1)(sass@1.77.8)))(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.1)(sass@1.77.8)))(svelte@5.0.0-next.175)) + version: 1.0.1(svelte@5.0.0-next.175)(sveltekit-superforms@2.17.0(@sveltejs/kit@2.5.25(@sveltejs/vite-plugin-svelte@3.1.2(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.2)(sass@1.77.8)))(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.2)(sass@1.77.8)))(svelte@5.0.0-next.175)) handlebars: specifier: ^4.7.8 version: 4.7.8 @@ -123,8 +123,8 @@ importers: specifier: ^2.1.1 version: 2.1.1 open-props: - specifier: ^1.7.5 - version: 1.7.5 + specifier: ^1.7.6 + version: 1.7.6 oslo: specifier: ^1.2.1 version: 1.2.1 @@ -157,10 +157,10 @@ importers: version: 2.5.2 tailwind-variants: specifier: ^0.2.1 - version: 0.2.1(tailwindcss@3.4.10(ts-node@10.9.2(@types/node@20.16.1)(typescript@5.5.4))) + version: 0.2.1(tailwindcss@3.4.10(ts-node@10.9.2(@types/node@20.16.2)(typescript@5.5.4))) tailwindcss-animate: specifier: ^1.0.7 - version: 1.0.7(tailwindcss@3.4.10(ts-node@10.9.2(@types/node@20.16.1)(typescript@5.5.4))) + version: 1.0.7(tailwindcss@3.4.10(ts-node@10.9.2(@types/node@20.16.2)(typescript@5.5.4))) tsyringe: specifier: ^4.8.0 version: 4.8.0 @@ -185,25 +185,25 @@ importers: version: 1.46.1 '@sveltejs/adapter-auto': specifier: ^3.2.4 - version: 3.2.4(@sveltejs/kit@2.5.24(@sveltejs/vite-plugin-svelte@3.1.2(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.1)(sass@1.77.8)))(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.1)(sass@1.77.8))) + version: 3.2.4(@sveltejs/kit@2.5.25(@sveltejs/vite-plugin-svelte@3.1.2(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.2)(sass@1.77.8)))(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.2)(sass@1.77.8))) '@sveltejs/enhanced-img': - specifier: ^0.3.3 - version: 0.3.3(rollup@4.18.1)(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.1)(sass@1.77.8)) + specifier: ^0.3.4 + version: 0.3.4(rollup@4.18.1)(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.2)(sass@1.77.8)) '@sveltejs/kit': - specifier: ^2.5.24 - version: 2.5.24(@sveltejs/vite-plugin-svelte@3.1.2(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.1)(sass@1.77.8)))(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.1)(sass@1.77.8)) + specifier: ^2.5.25 + version: 2.5.25(@sveltejs/vite-plugin-svelte@3.1.2(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.2)(sass@1.77.8)))(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.2)(sass@1.77.8)) '@sveltejs/vite-plugin-svelte': specifier: ^3.1.2 - version: 3.1.2(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.1)(sass@1.77.8)) + version: 3.1.2(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.2)(sass@1.77.8)) '@types/cookie': specifier: ^0.6.0 version: 0.6.0 '@types/node': - specifier: ^20.16.1 - version: 20.16.1 + specifier: ^20.16.2 + version: 20.16.2 '@types/pg': - specifier: ^8.11.6 - version: 8.11.6 + specifier: ^8.11.8 + version: 8.11.8 '@typescript-eslint/eslint-plugin': specifier: ^7.18.0 version: 7.18.0(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.5.4))(eslint@8.57.0)(typescript@5.5.4) @@ -224,7 +224,7 @@ importers: version: 9.1.0(eslint@8.57.0) eslint-plugin-svelte: specifier: ^2.43.0 - version: 2.43.0(eslint@8.57.0)(svelte@5.0.0-next.175)(ts-node@10.9.2(@types/node@20.16.1)(typescript@5.5.4)) + version: 2.43.0(eslint@8.57.0)(svelte@5.0.0-next.175)(ts-node@10.9.2(@types/node@20.16.2)(typescript@5.5.4)) just-clone: specifier: ^6.2.0 version: 6.2.0 @@ -287,19 +287,19 @@ importers: version: 2.0.1 sveltekit-flash-message: specifier: ^2.4.4 - version: 2.4.4(@sveltejs/kit@2.5.24(@sveltejs/vite-plugin-svelte@3.1.2(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.1)(sass@1.77.8)))(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.1)(sass@1.77.8)))(svelte@5.0.0-next.175) + version: 2.4.4(@sveltejs/kit@2.5.25(@sveltejs/vite-plugin-svelte@3.1.2(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.2)(sass@1.77.8)))(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.2)(sass@1.77.8)))(svelte@5.0.0-next.175) sveltekit-rate-limiter: specifier: ^0.5.2 - version: 0.5.2(@sveltejs/kit@2.5.24(@sveltejs/vite-plugin-svelte@3.1.2(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.1)(sass@1.77.8)))(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.1)(sass@1.77.8))) + version: 0.5.2(@sveltejs/kit@2.5.25(@sveltejs/vite-plugin-svelte@3.1.2(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.2)(sass@1.77.8)))(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.2)(sass@1.77.8))) sveltekit-superforms: specifier: ^2.17.0 - version: 2.17.0(@sveltejs/kit@2.5.24(@sveltejs/vite-plugin-svelte@3.1.2(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.1)(sass@1.77.8)))(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.1)(sass@1.77.8)))(svelte@5.0.0-next.175) + version: 2.17.0(@sveltejs/kit@2.5.25(@sveltejs/vite-plugin-svelte@3.1.2(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.2)(sass@1.77.8)))(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.2)(sass@1.77.8)))(svelte@5.0.0-next.175) tailwindcss: specifier: ^3.4.10 - version: 3.4.10(ts-node@10.9.2(@types/node@20.16.1)(typescript@5.5.4)) + version: 3.4.10(ts-node@10.9.2(@types/node@20.16.2)(typescript@5.5.4)) ts-node: specifier: ^10.9.2 - version: 10.9.2(@types/node@20.16.1)(typescript@5.5.4) + version: 10.9.2(@types/node@20.16.2)(typescript@5.5.4) tslib: specifier: ^2.7.0 version: 2.7.0 @@ -311,10 +311,10 @@ importers: version: 5.5.4 vite: specifier: ^5.4.2 - version: 5.4.2(@types/node@20.16.1)(sass@1.77.8) + version: 5.4.2(@types/node@20.16.2)(sass@1.77.8) vitest: specifier: ^1.6.0 - version: 1.6.0(@types/node@20.16.1)(sass@1.77.8) + version: 1.6.0(@types/node@20.16.2)(sass@1.77.8) zod: specifier: ^3.23.8 version: 3.23.8 @@ -2054,14 +2054,14 @@ packages: peerDependencies: '@sveltejs/kit': ^2.4.0 - '@sveltejs/enhanced-img@0.3.3': - resolution: {integrity: sha512-nsqJkVuYLUXARDLjMoGKAt4oLzwtY8X2E8rIl/TJl7ueLjpTISxrAhVRN3r8yMO+R+so4G6Taiix2mpiPpqZeg==} + '@sveltejs/enhanced-img@0.3.4': + resolution: {integrity: sha512-eX+ob5uWr0bTLMKeG9nhhM84aR88hqiLiyEfWZPX7ijhk/wlmYSUX9nOiaVHh2ct1U+Ju9Hhb90Copw+ZNOB8w==} peerDependencies: svelte: ^4.0.0 || ^5.0.0-next.0 vite: '>= 5.0.0' - '@sveltejs/kit@2.5.24': - resolution: {integrity: sha512-Nr2oxsCsDfEkdS/zzQQQbsPYTbu692Qs3/iE3L7VHzCVjG2+WujF9oMUozWI7GuX98KxYSoPMlAsfmDLSg44hQ==} + '@sveltejs/kit@2.5.25': + resolution: {integrity: sha512-5hBSEN8XEjDZ5+2bHkFh8Z0QyOk0C187cyb12aANe1c8aeKbfu5ZD5XaC2vEH4h0alJFDXPdUkXQBmeeXeMr1A==} engines: {node: '>=18.13'} hasBin: true peerDependencies: @@ -2114,12 +2114,15 @@ packages: '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} - '@types/node@20.16.1': - resolution: {integrity: sha512-zJDo7wEadFtSyNz5QITDfRcrhqDvQI1xQNQ0VoizPjM/dVAODqqIUWbJPkvsxmTI0MYRGRikcdjMPhOssnPejQ==} + '@types/node@20.16.2': + resolution: {integrity: sha512-91s/n4qUPV/wg8eE9KHYW1kouTfDk2FPGjXbBMfRWP/2vg1rCXNQL1OCabwGs0XSdukuK+MwCDXE30QpSeMUhQ==} '@types/pg@8.11.6': resolution: {integrity: sha512-/2WmmBXHLsfRqzfHW7BNZ8SbYzE8OSk7i3WjFYvfgRHj7S1xj+16Je5fUKv3lVdVzk/zn9TXOqf+avFCFIE0yQ==} + '@types/pg@8.11.8': + resolution: {integrity: sha512-IqpCf8/569txXN/HoP5i1LjXfKZWL76Yr2R77xgeIICUbAYHeoaEZFhYHo2uDftecLWrTJUq63JvQu8q3lnDyA==} + '@types/pug@2.0.10': resolution: {integrity: sha512-Sk/uYFOBAB7mb74XcpizmH0KOR2Pv3D2Hmrh1Dmy5BmK3MpdSa5kqZcg6EKBdklU0bFXX9gCfzvpnyUehrPIuA==} @@ -2402,8 +2405,8 @@ packages: resolution: {integrity: sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==} engines: {node: '>=6'} - bullmq@5.12.10: - resolution: {integrity: sha512-lXH8Caj+FvYHiBS0QBEpQOq57RcVuEPziBC5cBlWguCVNfn1UMSri22bRrynDKuof8o9XB43ctmYASUpoa0DeQ==} + bullmq@5.12.12: + resolution: {integrity: sha512-xrWKDj1ZwnGKmrlmFqF6Vmub3WqDFfdBcIRLCooIs5+jeVzbHK7/1usgYSFg2pZiwK6h6eMivTb9WvcKkNW/+w==} bytes@3.1.2: resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} @@ -3667,8 +3670,8 @@ packages: resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} engines: {node: '>=12'} - open-props@1.7.5: - resolution: {integrity: sha512-DajjLQDJgIa0i+QdB2q5M8lNLo2ICk+DbDh4TsqNsT1tAO8Zm8F7dndSkLMQkobT98lbvDMMpJWO8NT0ibjrjA==} + open-props@1.7.6: + resolution: {integrity: sha512-fE3E22x8lCf6gJrPO3L54NKrPaqQGxeKoXLz4JIXrhALD3Ua1kkE2kMw8HmlTKYF9BSLLMfoVfsT3UC1PJcaww==} openapi3-ts@4.3.3: resolution: {integrity: sha512-LKkzBGJcZ6wdvkKGMoSvpK+0cbN5Xc3XuYkJskO+vjEQWJgs1kgtyUk0pjf8KwPuysv323Er62F5P17XQl96Qg==} @@ -5851,9 +5854,9 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.4.15 - '@lucia-auth/adapter-drizzle@1.1.0(drizzle-orm@0.32.2(@neondatabase/serverless@0.9.4)(@types/pg@8.11.6)(pg@8.12.0)(postgres@3.4.4))(lucia@3.2.0)': + '@lucia-auth/adapter-drizzle@1.1.0(drizzle-orm@0.32.2(@neondatabase/serverless@0.9.4)(@types/pg@8.11.8)(pg@8.12.0)(postgres@3.4.4))(lucia@3.2.0)': dependencies: - drizzle-orm: 0.32.2(@neondatabase/serverless@0.9.4)(@types/pg@8.11.6)(pg@8.12.0)(postgres@3.4.4) + drizzle-orm: 0.32.2(@neondatabase/serverless@0.9.4)(@types/pg@8.11.8)(pg@8.12.0)(postgres@3.4.4) lucia: 3.2.0 '@lukeed/csprng@1.1.0': {} @@ -6298,41 +6301,41 @@ snapshots: '@sodaru/yup-to-json-schema@2.0.1': optional: true - '@sveltejs/adapter-auto@3.2.4(@sveltejs/kit@2.5.24(@sveltejs/vite-plugin-svelte@3.1.2(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.1)(sass@1.77.8)))(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.1)(sass@1.77.8)))': + '@sveltejs/adapter-auto@3.2.4(@sveltejs/kit@2.5.25(@sveltejs/vite-plugin-svelte@3.1.2(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.2)(sass@1.77.8)))(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.2)(sass@1.77.8)))': dependencies: - '@sveltejs/kit': 2.5.24(@sveltejs/vite-plugin-svelte@3.1.2(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.1)(sass@1.77.8)))(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.1)(sass@1.77.8)) + '@sveltejs/kit': 2.5.25(@sveltejs/vite-plugin-svelte@3.1.2(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.2)(sass@1.77.8)))(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.2)(sass@1.77.8)) import-meta-resolve: 4.1.0 - '@sveltejs/adapter-node@5.2.2(@sveltejs/kit@2.5.24(@sveltejs/vite-plugin-svelte@3.1.2(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.1)(sass@1.77.8)))(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.1)(sass@1.77.8)))': + '@sveltejs/adapter-node@5.2.2(@sveltejs/kit@2.5.25(@sveltejs/vite-plugin-svelte@3.1.2(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.2)(sass@1.77.8)))(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.2)(sass@1.77.8)))': dependencies: '@rollup/plugin-commonjs': 26.0.1(rollup@4.18.1) '@rollup/plugin-json': 6.1.0(rollup@4.18.1) '@rollup/plugin-node-resolve': 15.2.3(rollup@4.18.1) - '@sveltejs/kit': 2.5.24(@sveltejs/vite-plugin-svelte@3.1.2(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.1)(sass@1.77.8)))(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.1)(sass@1.77.8)) + '@sveltejs/kit': 2.5.25(@sveltejs/vite-plugin-svelte@3.1.2(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.2)(sass@1.77.8)))(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.2)(sass@1.77.8)) rollup: 4.18.1 - '@sveltejs/adapter-vercel@5.4.3(@sveltejs/kit@2.5.24(@sveltejs/vite-plugin-svelte@3.1.2(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.1)(sass@1.77.8)))(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.1)(sass@1.77.8)))': + '@sveltejs/adapter-vercel@5.4.3(@sveltejs/kit@2.5.25(@sveltejs/vite-plugin-svelte@3.1.2(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.2)(sass@1.77.8)))(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.2)(sass@1.77.8)))': dependencies: - '@sveltejs/kit': 2.5.24(@sveltejs/vite-plugin-svelte@3.1.2(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.1)(sass@1.77.8)))(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.1)(sass@1.77.8)) + '@sveltejs/kit': 2.5.25(@sveltejs/vite-plugin-svelte@3.1.2(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.2)(sass@1.77.8)))(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.2)(sass@1.77.8)) '@vercel/nft': 0.27.2 esbuild: 0.21.5 transitivePeerDependencies: - encoding - supports-color - '@sveltejs/enhanced-img@0.3.3(rollup@4.18.1)(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.1)(sass@1.77.8))': + '@sveltejs/enhanced-img@0.3.4(rollup@4.18.1)(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.2)(sass@1.77.8))': dependencies: magic-string: 0.30.10 svelte: 5.0.0-next.175 svelte-parse-markup: 0.1.5(svelte@5.0.0-next.175) - vite: 5.4.2(@types/node@20.16.1)(sass@1.77.8) + vite: 5.4.2(@types/node@20.16.2)(sass@1.77.8) vite-imagetools: 7.0.2(rollup@4.18.1) transitivePeerDependencies: - rollup - '@sveltejs/kit@2.5.24(@sveltejs/vite-plugin-svelte@3.1.2(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.1)(sass@1.77.8)))(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.1)(sass@1.77.8))': + '@sveltejs/kit@2.5.25(@sveltejs/vite-plugin-svelte@3.1.2(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.2)(sass@1.77.8)))(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.2)(sass@1.77.8))': dependencies: - '@sveltejs/vite-plugin-svelte': 3.1.2(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.1)(sass@1.77.8)) + '@sveltejs/vite-plugin-svelte': 3.1.2(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.2)(sass@1.77.8)) '@types/cookie': 0.6.0 cookie: 0.6.0 devalue: 5.0.0 @@ -6346,28 +6349,28 @@ snapshots: sirv: 2.0.4 svelte: 5.0.0-next.175 tiny-glob: 0.2.9 - vite: 5.4.2(@types/node@20.16.1)(sass@1.77.8) + vite: 5.4.2(@types/node@20.16.2)(sass@1.77.8) - '@sveltejs/vite-plugin-svelte-inspector@2.1.0(@sveltejs/vite-plugin-svelte@3.1.2(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.1)(sass@1.77.8)))(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.1)(sass@1.77.8))': + '@sveltejs/vite-plugin-svelte-inspector@2.1.0(@sveltejs/vite-plugin-svelte@3.1.2(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.2)(sass@1.77.8)))(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.2)(sass@1.77.8))': dependencies: - '@sveltejs/vite-plugin-svelte': 3.1.2(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.1)(sass@1.77.8)) + '@sveltejs/vite-plugin-svelte': 3.1.2(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.2)(sass@1.77.8)) debug: 4.3.4 svelte: 5.0.0-next.175 - vite: 5.4.2(@types/node@20.16.1)(sass@1.77.8) + vite: 5.4.2(@types/node@20.16.2)(sass@1.77.8) transitivePeerDependencies: - supports-color - '@sveltejs/vite-plugin-svelte@3.1.2(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.1)(sass@1.77.8))': + '@sveltejs/vite-plugin-svelte@3.1.2(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.2)(sass@1.77.8))': dependencies: - '@sveltejs/vite-plugin-svelte-inspector': 2.1.0(@sveltejs/vite-plugin-svelte@3.1.2(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.1)(sass@1.77.8)))(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.1)(sass@1.77.8)) + '@sveltejs/vite-plugin-svelte-inspector': 2.1.0(@sveltejs/vite-plugin-svelte@3.1.2(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.2)(sass@1.77.8)))(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.2)(sass@1.77.8)) debug: 4.3.4 deepmerge: 4.3.1 kleur: 4.1.5 magic-string: 0.30.10 svelte: 5.0.0-next.175 svelte-hmr: 0.16.0(svelte@5.0.0-next.175) - vite: 5.4.2(@types/node@20.16.1)(sass@1.77.8) - vitefu: 0.2.5(vite@5.4.2(@types/node@20.16.1)(sass@1.77.8)) + vite: 5.4.2(@types/node@20.16.2)(sass@1.77.8) + vitefu: 0.2.5(vite@5.4.2(@types/node@20.16.2)(sass@1.77.8)) transitivePeerDependencies: - supports-color @@ -6397,13 +6400,19 @@ snapshots: '@types/json-schema@7.0.15': optional: true - '@types/node@20.16.1': + '@types/node@20.16.2': dependencies: undici-types: 6.19.6 '@types/pg@8.11.6': dependencies: - '@types/node': 20.16.1 + '@types/node': 20.16.2 + pg-protocol: 1.6.1 + pg-types: 4.0.2 + + '@types/pg@8.11.8': + dependencies: + '@types/node': 20.16.2 pg-protocol: 1.6.1 pg-types: 4.0.2 @@ -6750,7 +6759,7 @@ snapshots: builtin-modules@3.3.0: {} - bullmq@5.12.10: + bullmq@5.12.12: dependencies: cron-parser: 4.9.0 ioredis: 5.4.1 @@ -7021,16 +7030,16 @@ snapshots: transitivePeerDependencies: - supports-color - drizzle-orm@0.32.2(@neondatabase/serverless@0.9.4)(@types/pg@8.11.6)(pg@8.12.0)(postgres@3.4.4): + drizzle-orm@0.32.2(@neondatabase/serverless@0.9.4)(@types/pg@8.11.8)(pg@8.12.0)(postgres@3.4.4): optionalDependencies: '@neondatabase/serverless': 0.9.4 - '@types/pg': 8.11.6 + '@types/pg': 8.11.8 pg: 8.12.0 postgres: 3.4.4 - drizzle-zod@0.5.1(drizzle-orm@0.32.2(@neondatabase/serverless@0.9.4)(@types/pg@8.11.6)(pg@8.12.0)(postgres@3.4.4))(zod@3.23.8): + drizzle-zod@0.5.1(drizzle-orm@0.32.2(@neondatabase/serverless@0.9.4)(@types/pg@8.11.8)(pg@8.12.0)(postgres@3.4.4))(zod@3.23.8): dependencies: - drizzle-orm: 0.32.2(@neondatabase/serverless@0.9.4)(@types/pg@8.11.6)(pg@8.12.0)(postgres@3.4.4) + drizzle-orm: 0.32.2(@neondatabase/serverless@0.9.4)(@types/pg@8.11.8)(pg@8.12.0)(postgres@3.4.4) zod: 3.23.8 eastasianwidth@0.2.0: {} @@ -7190,7 +7199,7 @@ snapshots: dependencies: eslint: 8.57.0 - eslint-plugin-svelte@2.43.0(eslint@8.57.0)(svelte@5.0.0-next.175)(ts-node@10.9.2(@types/node@20.16.1)(typescript@5.5.4)): + eslint-plugin-svelte@2.43.0(eslint@8.57.0)(svelte@5.0.0-next.175)(ts-node@10.9.2(@types/node@20.16.2)(typescript@5.5.4)): dependencies: '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) '@jridgewell/sourcemap-codec': 1.4.15 @@ -7199,7 +7208,7 @@ snapshots: esutils: 2.0.3 known-css-properties: 0.34.0 postcss: 8.4.41 - postcss-load-config: 3.1.4(postcss@8.4.41)(ts-node@10.9.2(@types/node@20.16.1)(typescript@5.5.4)) + postcss-load-config: 3.1.4(postcss@8.4.41)(ts-node@10.9.2(@types/node@20.16.2)(typescript@5.5.4)) postcss-safe-parser: 6.0.0(postcss@8.4.41) postcss-selector-parser: 6.1.0 semver: 7.6.2 @@ -7426,11 +7435,11 @@ snapshots: cross-spawn: 7.0.3 signal-exit: 4.1.0 - formsnap@1.0.1(svelte@5.0.0-next.175)(sveltekit-superforms@2.17.0(@sveltejs/kit@2.5.24(@sveltejs/vite-plugin-svelte@3.1.2(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.1)(sass@1.77.8)))(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.1)(sass@1.77.8)))(svelte@5.0.0-next.175)): + formsnap@1.0.1(svelte@5.0.0-next.175)(sveltekit-superforms@2.17.0(@sveltejs/kit@2.5.25(@sveltejs/vite-plugin-svelte@3.1.2(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.2)(sass@1.77.8)))(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.2)(sass@1.77.8)))(svelte@5.0.0-next.175)): dependencies: nanoid: 5.0.7 svelte: 5.0.0-next.175 - sveltekit-superforms: 2.17.0(@sveltejs/kit@2.5.24(@sveltejs/vite-plugin-svelte@3.1.2(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.1)(sass@1.77.8)))(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.1)(sass@1.77.8)))(svelte@5.0.0-next.175) + sveltekit-superforms: 2.17.0(@sveltejs/kit@2.5.25(@sveltejs/vite-plugin-svelte@3.1.2(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.2)(sass@1.77.8)))(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.2)(sass@1.77.8)))(svelte@5.0.0-next.175) forwarded@0.2.0: {} @@ -8003,7 +8012,7 @@ snapshots: dependencies: mimic-fn: 4.0.0 - open-props@1.7.5: {} + open-props@1.7.6: {} openapi3-ts@4.3.3: dependencies: @@ -8285,21 +8294,21 @@ snapshots: '@csstools/utilities': 1.0.0(postcss@8.4.41) postcss: 8.4.41 - postcss-load-config@3.1.4(postcss@8.4.41)(ts-node@10.9.2(@types/node@20.16.1)(typescript@5.5.4)): + postcss-load-config@3.1.4(postcss@8.4.41)(ts-node@10.9.2(@types/node@20.16.2)(typescript@5.5.4)): dependencies: lilconfig: 2.1.0 yaml: 1.10.2 optionalDependencies: postcss: 8.4.41 - ts-node: 10.9.2(@types/node@20.16.1)(typescript@5.5.4) + ts-node: 10.9.2(@types/node@20.16.2)(typescript@5.5.4) - postcss-load-config@4.0.2(postcss@8.4.41)(ts-node@10.9.2(@types/node@20.16.1)(typescript@5.5.4)): + postcss-load-config@4.0.2(postcss@8.4.41)(ts-node@10.9.2(@types/node@20.16.2)(typescript@5.5.4)): dependencies: lilconfig: 3.1.1 yaml: 2.4.3 optionalDependencies: postcss: 8.4.41 - ts-node: 10.9.2(@types/node@20.16.1)(typescript@5.5.4) + ts-node: 10.9.2(@types/node@20.16.2)(typescript@5.5.4) postcss-load-config@5.1.0(jiti@1.21.6)(postcss@8.4.41)(tsx@4.19.0): dependencies: @@ -9015,19 +9024,19 @@ snapshots: magic-string: 0.30.10 zimmerframe: 1.1.2 - sveltekit-flash-message@2.4.4(@sveltejs/kit@2.5.24(@sveltejs/vite-plugin-svelte@3.1.2(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.1)(sass@1.77.8)))(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.1)(sass@1.77.8)))(svelte@5.0.0-next.175): + sveltekit-flash-message@2.4.4(@sveltejs/kit@2.5.25(@sveltejs/vite-plugin-svelte@3.1.2(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.2)(sass@1.77.8)))(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.2)(sass@1.77.8)))(svelte@5.0.0-next.175): dependencies: - '@sveltejs/kit': 2.5.24(@sveltejs/vite-plugin-svelte@3.1.2(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.1)(sass@1.77.8)))(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.1)(sass@1.77.8)) + '@sveltejs/kit': 2.5.25(@sveltejs/vite-plugin-svelte@3.1.2(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.2)(sass@1.77.8)))(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.2)(sass@1.77.8)) svelte: 5.0.0-next.175 - sveltekit-rate-limiter@0.5.2(@sveltejs/kit@2.5.24(@sveltejs/vite-plugin-svelte@3.1.2(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.1)(sass@1.77.8)))(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.1)(sass@1.77.8))): + sveltekit-rate-limiter@0.5.2(@sveltejs/kit@2.5.25(@sveltejs/vite-plugin-svelte@3.1.2(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.2)(sass@1.77.8)))(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.2)(sass@1.77.8))): dependencies: '@isaacs/ttlcache': 1.4.1 - '@sveltejs/kit': 2.5.24(@sveltejs/vite-plugin-svelte@3.1.2(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.1)(sass@1.77.8)))(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.1)(sass@1.77.8)) + '@sveltejs/kit': 2.5.25(@sveltejs/vite-plugin-svelte@3.1.2(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.2)(sass@1.77.8)))(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.2)(sass@1.77.8)) - sveltekit-superforms@2.17.0(@sveltejs/kit@2.5.24(@sveltejs/vite-plugin-svelte@3.1.2(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.1)(sass@1.77.8)))(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.1)(sass@1.77.8)))(svelte@5.0.0-next.175): + sveltekit-superforms@2.17.0(@sveltejs/kit@2.5.25(@sveltejs/vite-plugin-svelte@3.1.2(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.2)(sass@1.77.8)))(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.2)(sass@1.77.8)))(svelte@5.0.0-next.175): dependencies: - '@sveltejs/kit': 2.5.24(@sveltejs/vite-plugin-svelte@3.1.2(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.1)(sass@1.77.8)))(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.1)(sass@1.77.8)) + '@sveltejs/kit': 2.5.25(@sveltejs/vite-plugin-svelte@3.1.2(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.2)(sass@1.77.8)))(svelte@5.0.0-next.175)(vite@5.4.2(@types/node@20.16.2)(sass@1.77.8)) devalue: 5.0.0 just-clone: 6.2.0 memoize-weak: 1.0.2 @@ -9052,16 +9061,16 @@ snapshots: tailwind-merge@2.5.2: {} - tailwind-variants@0.2.1(tailwindcss@3.4.10(ts-node@10.9.2(@types/node@20.16.1)(typescript@5.5.4))): + tailwind-variants@0.2.1(tailwindcss@3.4.10(ts-node@10.9.2(@types/node@20.16.2)(typescript@5.5.4))): dependencies: tailwind-merge: 2.5.2 - tailwindcss: 3.4.10(ts-node@10.9.2(@types/node@20.16.1)(typescript@5.5.4)) + tailwindcss: 3.4.10(ts-node@10.9.2(@types/node@20.16.2)(typescript@5.5.4)) - tailwindcss-animate@1.0.7(tailwindcss@3.4.10(ts-node@10.9.2(@types/node@20.16.1)(typescript@5.5.4))): + tailwindcss-animate@1.0.7(tailwindcss@3.4.10(ts-node@10.9.2(@types/node@20.16.2)(typescript@5.5.4))): dependencies: - tailwindcss: 3.4.10(ts-node@10.9.2(@types/node@20.16.1)(typescript@5.5.4)) + tailwindcss: 3.4.10(ts-node@10.9.2(@types/node@20.16.2)(typescript@5.5.4)) - tailwindcss@3.4.10(ts-node@10.9.2(@types/node@20.16.1)(typescript@5.5.4)): + tailwindcss@3.4.10(ts-node@10.9.2(@types/node@20.16.2)(typescript@5.5.4)): dependencies: '@alloc/quick-lru': 5.2.0 arg: 5.0.2 @@ -9080,7 +9089,7 @@ snapshots: postcss: 8.4.41 postcss-import: 15.1.0(postcss@8.4.41) postcss-js: 4.0.1(postcss@8.4.41) - postcss-load-config: 4.0.2(postcss@8.4.41)(ts-node@10.9.2(@types/node@20.16.1)(typescript@5.5.4)) + postcss-load-config: 4.0.2(postcss@8.4.41)(ts-node@10.9.2(@types/node@20.16.2)(typescript@5.5.4)) postcss-nested: 6.0.1(postcss@8.4.41) postcss-selector-parser: 6.1.0 resolve: 1.22.8 @@ -9147,14 +9156,14 @@ snapshots: ts-interface-checker@0.1.13: {} - ts-node@10.9.2(@types/node@20.16.1)(typescript@5.5.4): + ts-node@10.9.2(@types/node@20.16.2)(typescript@5.5.4): dependencies: '@cspotcode/source-map-support': 0.8.1 '@tsconfig/node10': 1.0.11 '@tsconfig/node12': 1.0.11 '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.4 - '@types/node': 20.16.1 + '@types/node': 20.16.2 acorn: 8.11.3 acorn-walk: 8.3.2 arg: 4.1.3 @@ -9263,13 +9272,13 @@ snapshots: transitivePeerDependencies: - rollup - vite-node@1.6.0(@types/node@20.16.1)(sass@1.77.8): + vite-node@1.6.0(@types/node@20.16.2)(sass@1.77.8): dependencies: cac: 6.7.14 debug: 4.3.4 pathe: 1.1.2 picocolors: 1.0.0 - vite: 5.4.2(@types/node@20.16.1)(sass@1.77.8) + vite: 5.4.2(@types/node@20.16.2)(sass@1.77.8) transitivePeerDependencies: - '@types/node' - less @@ -9281,21 +9290,21 @@ snapshots: - supports-color - terser - vite@5.4.2(@types/node@20.16.1)(sass@1.77.8): + vite@5.4.2(@types/node@20.16.2)(sass@1.77.8): dependencies: esbuild: 0.21.5 postcss: 8.4.41 rollup: 4.21.0 optionalDependencies: - '@types/node': 20.16.1 + '@types/node': 20.16.2 fsevents: 2.3.3 sass: 1.77.8 - vitefu@0.2.5(vite@5.4.2(@types/node@20.16.1)(sass@1.77.8)): + vitefu@0.2.5(vite@5.4.2(@types/node@20.16.2)(sass@1.77.8)): optionalDependencies: - vite: 5.4.2(@types/node@20.16.1)(sass@1.77.8) + vite: 5.4.2(@types/node@20.16.2)(sass@1.77.8) - vitest@1.6.0(@types/node@20.16.1)(sass@1.77.8): + vitest@1.6.0(@types/node@20.16.2)(sass@1.77.8): dependencies: '@vitest/expect': 1.6.0 '@vitest/runner': 1.6.0 @@ -9314,11 +9323,11 @@ snapshots: strip-literal: 2.1.0 tinybench: 2.8.0 tinypool: 0.8.4 - vite: 5.4.2(@types/node@20.16.1)(sass@1.77.8) - vite-node: 1.6.0(@types/node@20.16.1)(sass@1.77.8) + vite: 5.4.2(@types/node@20.16.2)(sass@1.77.8) + vite-node: 1.6.0(@types/node@20.16.2)(sass@1.77.8) why-is-node-running: 2.2.2 optionalDependencies: - '@types/node': 20.16.1 + '@types/node': 20.16.2 transitivePeerDependencies: - less - lightningcss diff --git a/src/lib/dtos/verify-totp.dto.ts b/src/lib/dtos/verify-totp.dto.ts new file mode 100644 index 0000000..cd5f886 --- /dev/null +++ b/src/lib/dtos/verify-totp.dto.ts @@ -0,0 +1,11 @@ +import { z } from "zod"; + +export const verifyTotpDto = z.object({ + code: z + .string() + .trim() + .min(6, { message: 'Must be at least 6 characters' }) + .max(6, { message: 'Must be less than 6 characters' }), +}); + +export type VerifyTotpDto = z.infer; \ No newline at end of file diff --git a/src/lib/server/api/controllers/iam.controller.ts b/src/lib/server/api/controllers/iam.controller.ts index d872758..0f34305 100644 --- a/src/lib/server/api/controllers/iam.controller.ts +++ b/src/lib/server/api/controllers/iam.controller.ts @@ -33,7 +33,7 @@ export class IamController implements Controller { const { firstName, lastName, username } = c.req.valid('json') const updatedUser = await this.iamService.updateProfile(user.id, { firstName, lastName, username }) if (!updatedUser) { - return c.json("Username already in use", StatusCodes.BAD_REQUEST); + return c.json('Username already in use', StatusCodes.BAD_REQUEST) } return c.json({ user: updatedUser }, StatusCodes.OK) }) @@ -42,16 +42,17 @@ export class IamController implements Controller { const { password } = c.req.valid('json') const passwordVerified = await this.iamService.verifyPassword(user.id, { password }) if (!passwordVerified) { + console.log('Incorrect password') return c.json('Incorrect password', StatusCodes.BAD_REQUEST) } - return c.json({ }, StatusCodes.OK) + return c.json({}, StatusCodes.OK) }) .post('/update/email', requireAuth, zValidator('json', updateEmailDto), limiter({ limit: 10, minutes: 60 }), async (c) => { const user = c.var.user const { email } = c.req.valid('json') const updatedUser = await this.iamService.updateEmail(user.id, { email }) if (!updatedUser) { - return c.json("Email already in use", StatusCodes.BAD_REQUEST); + return c.json('Email already in use', StatusCodes.BAD_REQUEST) } return c.json({ user: updatedUser }, StatusCodes.OK) }) diff --git a/src/lib/server/api/controllers/mfa.controller.ts b/src/lib/server/api/controllers/mfa.controller.ts index 7c72abf..dbbc13c 100644 --- a/src/lib/server/api/controllers/mfa.controller.ts +++ b/src/lib/server/api/controllers/mfa.controller.ts @@ -1,39 +1,62 @@ -import 'reflect-metadata'; -import { Hono } from 'hono'; -import { inject, injectable } from 'tsyringe'; -import { requireAuth } from "../middleware/auth.middleware"; -import type { HonoTypes } from '../types'; -import type { Controller } from '$lib/server/api/interfaces/controller.interface'; -import { TotpService } from '$lib/server/api/services/totp.service'; -import {StatusCodes} from "$lib/constants/status-codes"; +import 'reflect-metadata' +import { inject, injectable } from 'tsyringe' +import { Hono } from 'hono' +import { zValidator } from '@hono/zod-validator' +import { requireAuth } from '../middleware/auth.middleware' +import type { HonoTypes } from '../types' +import type { Controller } from '$lib/server/api/interfaces/controller.interface' +import { TotpService } from '$lib/server/api/services/totp.service' +import { StatusCodes } from '$lib/constants/status-codes' +import { verifyTotpDto } from '$lib/dtos/verify-totp.dto' +import { UsersService } from '../services/users.service' +import { CredentialsType } from '$db/tables' @injectable() export class MfaController implements Controller { - controller = new Hono(); + controller = new Hono() constructor( - @inject(TotpService) private readonly totpService: TotpService - ) { - } - + @inject(TotpService) private readonly totpService: TotpService, + @inject(UsersService) private readonly usersService: UsersService, + ) {} routes() { return this.controller .get('/totp', requireAuth, async (c) => { - const user = c.var.user; - const totpCredential = await this.totpService.findOneByUserId(user.id); - return c.json({ totpCredential }); + const user = c.var.user + const totpCredential = await this.totpService.findOneByUserId(user.id) + return c.json({ totpCredential }) }) .post('/totp', requireAuth, async (c) => { - const user = c.var.user; - const totpCredential = await this.totpService.create(user.id); + const user = c.var.user + const totpCredential = await this.totpService.create(user.id) return c.json({ totpCredential }) }) .delete('/totp', requireAuth, async (c) => { - const user = c.var.user; - await this.totpService.deleteOneByUserId(user.id); - return c.status(StatusCodes.NO_CONTENT); - }); + const user = c.var.user + try { + await this.totpService.deleteOneByUserIdAndType(user.id, CredentialsType.TOTP) + console.log('TOTP deleted') + return c.body(null, StatusCodes.NO_CONTENT) + } catch (e) { + console.error(e) + return c.status(StatusCodes.INTERNAL_SERVER_ERROR) + } + }) + .post('/totp/verify', requireAuth, zValidator('json', verifyTotpDto), async (c) => { + try { + const user = c.var.user + const { code } = c.req.valid('json') + const verified = await this.totpService.verify(user.id, code) + if (verified) { + this.usersService.updateUser(user.id, { mfa_enabled: true }) + return c.json({}, StatusCodes.OK) + } + return c.json({}, StatusCodes.BAD_REQUEST) + } catch (e) { + console.error(e) + return c.status(StatusCodes.INTERNAL_SERVER_ERROR) + } + }) } - -} \ No newline at end of file +} diff --git a/src/lib/server/api/infrastructure/auth/lucia.ts b/src/lib/server/api/infrastructure/auth/lucia.ts index 9fb1eb9..8141cb6 100644 --- a/src/lib/server/api/infrastructure/auth/lucia.ts +++ b/src/lib/server/api/infrastructure/auth/lucia.ts @@ -23,6 +23,7 @@ export const lucia = new Lucia(adapter, { email: attributes.email, firstName: attributes.first_name, lastName: attributes.last_name, + mfa_enabled: attributes.mfa_enabled, theme: attributes.theme, }; }, @@ -56,6 +57,7 @@ declare module 'lucia' { email: string; first_name: string; last_name: string; + mfa_enabled: boolean; theme: string; } } diff --git a/src/lib/server/api/infrastructure/database/migrations/0004_heavy_sphinx.sql b/src/lib/server/api/infrastructure/database/migrations/0004_heavy_sphinx.sql new file mode 100644 index 0000000..70a7029 --- /dev/null +++ b/src/lib/server/api/infrastructure/database/migrations/0004_heavy_sphinx.sql @@ -0,0 +1 @@ +ALTER TABLE "users" ADD COLUMN "enabled" boolean DEFAULT false NOT NULL; \ No newline at end of file diff --git a/src/lib/server/api/infrastructure/database/migrations/0005_true_mathemanic.sql b/src/lib/server/api/infrastructure/database/migrations/0005_true_mathemanic.sql new file mode 100644 index 0000000..a37a946 --- /dev/null +++ b/src/lib/server/api/infrastructure/database/migrations/0005_true_mathemanic.sql @@ -0,0 +1 @@ +ALTER TABLE "users" RENAME COLUMN "enabled" TO "mfa_enabled"; \ No newline at end of file diff --git a/src/lib/server/api/infrastructure/database/migrations/meta/0004_snapshot.json b/src/lib/server/api/infrastructure/database/migrations/meta/0004_snapshot.json new file mode 100644 index 0000000..11e04b2 --- /dev/null +++ b/src/lib/server/api/infrastructure/database/migrations/meta/0004_snapshot.json @@ -0,0 +1,1863 @@ +{ + "id": "ee44907c-0c2a-4aa0-8e3a-59cf74321c7a", + "prevId": "5a4165ba-0d5a-4a58-aa14-e68e3e41a181", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.categories": { + "name": "categories", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "cuid": { + "name": "cuid", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "categories_cuid_unique": { + "name": "categories_cuid_unique", + "nullsNotDistinct": false, + "columns": [ + "cuid" + ] + } + } + }, + "public.categories_to_external_ids": { + "name": "categories_to_external_ids", + "schema": "", + "columns": { + "category_id": { + "name": "category_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "external_id": { + "name": "external_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "categories_to_external_ids_category_id_categories_id_fk": { + "name": "categories_to_external_ids_category_id_categories_id_fk", + "tableFrom": "categories_to_external_ids", + "tableTo": "categories", + "columnsFrom": [ + "category_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "cascade" + }, + "categories_to_external_ids_external_id_external_ids_id_fk": { + "name": "categories_to_external_ids_external_id_external_ids_id_fk", + "tableFrom": "categories_to_external_ids", + "tableTo": "external_ids", + "columnsFrom": [ + "external_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": { + "categories_to_external_ids_category_id_external_id_pk": { + "name": "categories_to_external_ids_category_id_external_id_pk", + "columns": [ + "category_id", + "external_id" + ] + } + }, + "uniqueConstraints": {} + }, + "public.categories_to_games": { + "name": "categories_to_games", + "schema": "", + "columns": { + "category_id": { + "name": "category_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "game_id": { + "name": "game_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "categories_to_games_category_id_categories_id_fk": { + "name": "categories_to_games_category_id_categories_id_fk", + "tableFrom": "categories_to_games", + "tableTo": "categories", + "columnsFrom": [ + "category_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "cascade" + }, + "categories_to_games_game_id_games_id_fk": { + "name": "categories_to_games_game_id_games_id_fk", + "tableFrom": "categories_to_games", + "tableTo": "games", + "columnsFrom": [ + "game_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": { + "categories_to_games_category_id_game_id_pk": { + "name": "categories_to_games_category_id_game_id_pk", + "columns": [ + "category_id", + "game_id" + ] + } + }, + "uniqueConstraints": {} + }, + "public.collection_items": { + "name": "collection_items", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "cuid": { + "name": "cuid", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "collection_id": { + "name": "collection_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "game_id": { + "name": "game_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "times_played": { + "name": "times_played", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "collection_items_collection_id_collections_id_fk": { + "name": "collection_items_collection_id_collections_id_fk", + "tableFrom": "collection_items", + "tableTo": "collections", + "columnsFrom": [ + "collection_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "collection_items_game_id_games_id_fk": { + "name": "collection_items_game_id_games_id_fk", + "tableFrom": "collection_items", + "tableTo": "games", + "columnsFrom": [ + "game_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "collection_items_cuid_unique": { + "name": "collection_items_cuid_unique", + "nullsNotDistinct": false, + "columns": [ + "cuid" + ] + } + } + }, + "public.collections": { + "name": "collections", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "cuid": { + "name": "cuid", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'My Collection'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "collections_user_id_users_id_fk": { + "name": "collections_user_id_users_id_fk", + "tableFrom": "collections", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "collections_cuid_unique": { + "name": "collections_cuid_unique", + "nullsNotDistinct": false, + "columns": [ + "cuid" + ] + } + } + }, + "public.credentials": { + "name": "credentials", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'password'" + }, + "secret_data": { + "name": "secret_data", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "credentials_user_id_users_id_fk": { + "name": "credentials_user_id_users_id_fk", + "tableFrom": "credentials", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.expansions": { + "name": "expansions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "cuid": { + "name": "cuid", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "base_game_id": { + "name": "base_game_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "game_id": { + "name": "game_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "expansions_base_game_id_games_id_fk": { + "name": "expansions_base_game_id_games_id_fk", + "tableFrom": "expansions", + "tableTo": "games", + "columnsFrom": [ + "base_game_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "cascade" + }, + "expansions_game_id_games_id_fk": { + "name": "expansions_game_id_games_id_fk", + "tableFrom": "expansions", + "tableTo": "games", + "columnsFrom": [ + "game_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "expansions_cuid_unique": { + "name": "expansions_cuid_unique", + "nullsNotDistinct": false, + "columns": [ + "cuid" + ] + } + } + }, + "public.external_ids": { + "name": "external_ids", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "cuid": { + "name": "cuid", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "type": { + "name": "type", + "type": "external_id_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "external_id": { + "name": "external_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "external_ids_cuid_unique": { + "name": "external_ids_cuid_unique", + "nullsNotDistinct": false, + "columns": [ + "cuid" + ] + } + } + }, + "public.federated_identity": { + "name": "federated_identity", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "identity_provider": { + "name": "identity_provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "federated_user_id": { + "name": "federated_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "federated_username": { + "name": "federated_username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "federated_identity_user_id_users_id_fk": { + "name": "federated_identity_user_id_users_id_fk", + "tableFrom": "federated_identity", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.games": { + "name": "games", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "cuid": { + "name": "cuid", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "year_published": { + "name": "year_published", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "min_players": { + "name": "min_players", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "max_players": { + "name": "max_players", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "playtime": { + "name": "playtime", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "min_playtime": { + "name": "min_playtime", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "max_playtime": { + "name": "max_playtime", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "min_age": { + "name": "min_age", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "image_url": { + "name": "image_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "thumb_url": { + "name": "thumb_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_sync_at": { + "name": "last_sync_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "search_index": { + "name": "search_index", + "columns": [ + { + "expression": "(\n\t\t\t\tsetweight(to_tsvector('english', \"name\"), 'A') ||\n setweight(to_tsvector('english', \"slug\"), 'B')\n )", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "games_cuid_unique": { + "name": "games_cuid_unique", + "nullsNotDistinct": false, + "columns": [ + "cuid" + ] + } + } + }, + "public.games_to_external_ids": { + "name": "games_to_external_ids", + "schema": "", + "columns": { + "game_id": { + "name": "game_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "external_id": { + "name": "external_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "games_to_external_ids_game_id_games_id_fk": { + "name": "games_to_external_ids_game_id_games_id_fk", + "tableFrom": "games_to_external_ids", + "tableTo": "games", + "columnsFrom": [ + "game_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "cascade" + }, + "games_to_external_ids_external_id_external_ids_id_fk": { + "name": "games_to_external_ids_external_id_external_ids_id_fk", + "tableFrom": "games_to_external_ids", + "tableTo": "external_ids", + "columnsFrom": [ + "external_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": { + "games_to_external_ids_game_id_external_id_pk": { + "name": "games_to_external_ids_game_id_external_id_pk", + "columns": [ + "game_id", + "external_id" + ] + } + }, + "uniqueConstraints": {} + }, + "public.mechanics": { + "name": "mechanics", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "cuid": { + "name": "cuid", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "mechanics_cuid_unique": { + "name": "mechanics_cuid_unique", + "nullsNotDistinct": false, + "columns": [ + "cuid" + ] + } + } + }, + "public.mechanics_to_external_ids": { + "name": "mechanics_to_external_ids", + "schema": "", + "columns": { + "mechanic_id": { + "name": "mechanic_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "external_id": { + "name": "external_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "mechanics_to_external_ids_mechanic_id_mechanics_id_fk": { + "name": "mechanics_to_external_ids_mechanic_id_mechanics_id_fk", + "tableFrom": "mechanics_to_external_ids", + "tableTo": "mechanics", + "columnsFrom": [ + "mechanic_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "cascade" + }, + "mechanics_to_external_ids_external_id_external_ids_id_fk": { + "name": "mechanics_to_external_ids_external_id_external_ids_id_fk", + "tableFrom": "mechanics_to_external_ids", + "tableTo": "external_ids", + "columnsFrom": [ + "external_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": { + "mechanics_to_external_ids_mechanic_id_external_id_pk": { + "name": "mechanics_to_external_ids_mechanic_id_external_id_pk", + "columns": [ + "mechanic_id", + "external_id" + ] + } + }, + "uniqueConstraints": {} + }, + "public.mechanics_to_games": { + "name": "mechanics_to_games", + "schema": "", + "columns": { + "mechanic_id": { + "name": "mechanic_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "game_id": { + "name": "game_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "mechanics_to_games_mechanic_id_mechanics_id_fk": { + "name": "mechanics_to_games_mechanic_id_mechanics_id_fk", + "tableFrom": "mechanics_to_games", + "tableTo": "mechanics", + "columnsFrom": [ + "mechanic_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "cascade" + }, + "mechanics_to_games_game_id_games_id_fk": { + "name": "mechanics_to_games_game_id_games_id_fk", + "tableFrom": "mechanics_to_games", + "tableTo": "games", + "columnsFrom": [ + "game_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": { + "mechanics_to_games_mechanic_id_game_id_pk": { + "name": "mechanics_to_games_mechanic_id_game_id_pk", + "columns": [ + "mechanic_id", + "game_id" + ] + } + }, + "uniqueConstraints": {} + }, + "public.password_reset_tokens": { + "name": "password_reset_tokens", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "password_reset_tokens_user_id_users_id_fk": { + "name": "password_reset_tokens_user_id_users_id_fk", + "tableFrom": "password_reset_tokens", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.publishers": { + "name": "publishers", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "cuid": { + "name": "cuid", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "publishers_cuid_unique": { + "name": "publishers_cuid_unique", + "nullsNotDistinct": false, + "columns": [ + "cuid" + ] + } + } + }, + "public.publishers_to_external_ids": { + "name": "publishers_to_external_ids", + "schema": "", + "columns": { + "publisher_id": { + "name": "publisher_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "external_id": { + "name": "external_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "publishers_to_external_ids_publisher_id_publishers_id_fk": { + "name": "publishers_to_external_ids_publisher_id_publishers_id_fk", + "tableFrom": "publishers_to_external_ids", + "tableTo": "publishers", + "columnsFrom": [ + "publisher_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "cascade" + }, + "publishers_to_external_ids_external_id_external_ids_id_fk": { + "name": "publishers_to_external_ids_external_id_external_ids_id_fk", + "tableFrom": "publishers_to_external_ids", + "tableTo": "external_ids", + "columnsFrom": [ + "external_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": { + "publishers_to_external_ids_publisher_id_external_id_pk": { + "name": "publishers_to_external_ids_publisher_id_external_id_pk", + "columns": [ + "publisher_id", + "external_id" + ] + } + }, + "uniqueConstraints": {} + }, + "public.publishers_to_games": { + "name": "publishers_to_games", + "schema": "", + "columns": { + "publisher_id": { + "name": "publisher_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "game_id": { + "name": "game_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "publishers_to_games_publisher_id_publishers_id_fk": { + "name": "publishers_to_games_publisher_id_publishers_id_fk", + "tableFrom": "publishers_to_games", + "tableTo": "publishers", + "columnsFrom": [ + "publisher_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "cascade" + }, + "publishers_to_games_game_id_games_id_fk": { + "name": "publishers_to_games_game_id_games_id_fk", + "tableFrom": "publishers_to_games", + "tableTo": "games", + "columnsFrom": [ + "game_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": { + "publishers_to_games_publisher_id_game_id_pk": { + "name": "publishers_to_games_publisher_id_game_id_pk", + "columns": [ + "publisher_id", + "game_id" + ] + } + }, + "uniqueConstraints": {} + }, + "public.recovery_codes": { + "name": "recovery_codes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "code": { + "name": "code", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "used": { + "name": "used", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "recovery_codes_user_id_users_id_fk": { + "name": "recovery_codes_user_id_users_id_fk", + "tableFrom": "recovery_codes", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.roles": { + "name": "roles", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "cuid": { + "name": "cuid", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "roles_cuid_unique": { + "name": "roles_cuid_unique", + "nullsNotDistinct": false, + "columns": [ + "cuid" + ] + }, + "roles_name_unique": { + "name": "roles_name_unique", + "nullsNotDistinct": false, + "columns": [ + "name" + ] + } + } + }, + "public.sessions": { + "name": "sessions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "ip_country": { + "name": "ip_country", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "ip_address": { + "name": "ip_address", + "type": "text", + "primaryKey": 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": {}, + "foreignKeys": { + "sessions_user_id_users_id_fk": { + "name": "sessions_user_id_users_id_fk", + "tableFrom": "sessions", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.two_factor": { + "name": "two_factor", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "cuid": { + "name": "cuid", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "secret": { + "name": "secret", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "initiated_time": { + "name": "initiated_time", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "two_factor_user_id_users_id_fk": { + "name": "two_factor_user_id_users_id_fk", + "tableFrom": "two_factor", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "two_factor_cuid_unique": { + "name": "two_factor_cuid_unique", + "nullsNotDistinct": false, + "columns": [ + "cuid" + ] + }, + "two_factor_user_id_unique": { + "name": "two_factor_user_id_unique", + "nullsNotDistinct": false, + "columns": [ + "user_id" + ] + } + } + }, + "public.user_roles": { + "name": "user_roles", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "cuid": { + "name": "cuid", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "role_id": { + "name": "role_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "primary": { + "name": "primary", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "user_roles_user_id_users_id_fk": { + "name": "user_roles_user_id_users_id_fk", + "tableFrom": "user_roles", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "user_roles_role_id_roles_id_fk": { + "name": "user_roles_role_id_roles_id_fk", + "tableFrom": "user_roles", + "tableTo": "roles", + "columnsFrom": [ + "role_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "user_roles_cuid_unique": { + "name": "user_roles_cuid_unique", + "nullsNotDistinct": false, + "columns": [ + "cuid" + ] + } + } + }, + "public.users": { + "name": "users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "cuid": { + "name": "cuid", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "first_name": { + "name": "first_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_name": { + "name": "last_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "verified": { + "name": "verified", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "receive_email": { + "name": "receive_email", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "theme": { + "name": "theme", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'system'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "users_cuid_unique": { + "name": "users_cuid_unique", + "nullsNotDistinct": false, + "columns": [ + "cuid" + ] + }, + "users_username_unique": { + "name": "users_username_unique", + "nullsNotDistinct": false, + "columns": [ + "username" + ] + }, + "users_email_unique": { + "name": "users_email_unique", + "nullsNotDistinct": false, + "columns": [ + "email" + ] + } + } + }, + "public.wishlist_items": { + "name": "wishlist_items", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "cuid": { + "name": "cuid", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "wishlist_id": { + "name": "wishlist_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "game_id": { + "name": "game_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "wishlist_items_wishlist_id_wishlists_id_fk": { + "name": "wishlist_items_wishlist_id_wishlists_id_fk", + "tableFrom": "wishlist_items", + "tableTo": "wishlists", + "columnsFrom": [ + "wishlist_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "wishlist_items_game_id_games_id_fk": { + "name": "wishlist_items_game_id_games_id_fk", + "tableFrom": "wishlist_items", + "tableTo": "games", + "columnsFrom": [ + "game_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "wishlist_items_cuid_unique": { + "name": "wishlist_items_cuid_unique", + "nullsNotDistinct": false, + "columns": [ + "cuid" + ] + } + } + }, + "public.wishlists": { + "name": "wishlists", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "cuid": { + "name": "cuid", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'My Wishlist'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "wishlists_user_id_users_id_fk": { + "name": "wishlists_user_id_users_id_fk", + "tableFrom": "wishlists", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "wishlists_cuid_unique": { + "name": "wishlists_cuid_unique", + "nullsNotDistinct": false, + "columns": [ + "cuid" + ] + } + } + } + }, + "enums": { + "public.external_id_type": { + "name": "external_id_type", + "schema": "public", + "values": [ + "game", + "category", + "mechanic", + "publisher", + "designer", + "artist" + ] + } + }, + "schemas": {}, + "sequences": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/src/lib/server/api/infrastructure/database/migrations/meta/0005_snapshot.json b/src/lib/server/api/infrastructure/database/migrations/meta/0005_snapshot.json new file mode 100644 index 0000000..c82db66 --- /dev/null +++ b/src/lib/server/api/infrastructure/database/migrations/meta/0005_snapshot.json @@ -0,0 +1,1863 @@ +{ + "id": "b7874689-9dc9-4171-9115-5b6333a0f995", + "prevId": "ee44907c-0c2a-4aa0-8e3a-59cf74321c7a", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.categories": { + "name": "categories", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "cuid": { + "name": "cuid", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "categories_cuid_unique": { + "name": "categories_cuid_unique", + "nullsNotDistinct": false, + "columns": [ + "cuid" + ] + } + } + }, + "public.categories_to_external_ids": { + "name": "categories_to_external_ids", + "schema": "", + "columns": { + "category_id": { + "name": "category_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "external_id": { + "name": "external_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "categories_to_external_ids_category_id_categories_id_fk": { + "name": "categories_to_external_ids_category_id_categories_id_fk", + "tableFrom": "categories_to_external_ids", + "tableTo": "categories", + "columnsFrom": [ + "category_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "cascade" + }, + "categories_to_external_ids_external_id_external_ids_id_fk": { + "name": "categories_to_external_ids_external_id_external_ids_id_fk", + "tableFrom": "categories_to_external_ids", + "tableTo": "external_ids", + "columnsFrom": [ + "external_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": { + "categories_to_external_ids_category_id_external_id_pk": { + "name": "categories_to_external_ids_category_id_external_id_pk", + "columns": [ + "category_id", + "external_id" + ] + } + }, + "uniqueConstraints": {} + }, + "public.categories_to_games": { + "name": "categories_to_games", + "schema": "", + "columns": { + "category_id": { + "name": "category_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "game_id": { + "name": "game_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "categories_to_games_category_id_categories_id_fk": { + "name": "categories_to_games_category_id_categories_id_fk", + "tableFrom": "categories_to_games", + "tableTo": "categories", + "columnsFrom": [ + "category_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "cascade" + }, + "categories_to_games_game_id_games_id_fk": { + "name": "categories_to_games_game_id_games_id_fk", + "tableFrom": "categories_to_games", + "tableTo": "games", + "columnsFrom": [ + "game_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": { + "categories_to_games_category_id_game_id_pk": { + "name": "categories_to_games_category_id_game_id_pk", + "columns": [ + "category_id", + "game_id" + ] + } + }, + "uniqueConstraints": {} + }, + "public.collection_items": { + "name": "collection_items", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "cuid": { + "name": "cuid", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "collection_id": { + "name": "collection_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "game_id": { + "name": "game_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "times_played": { + "name": "times_played", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "collection_items_collection_id_collections_id_fk": { + "name": "collection_items_collection_id_collections_id_fk", + "tableFrom": "collection_items", + "tableTo": "collections", + "columnsFrom": [ + "collection_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "collection_items_game_id_games_id_fk": { + "name": "collection_items_game_id_games_id_fk", + "tableFrom": "collection_items", + "tableTo": "games", + "columnsFrom": [ + "game_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "collection_items_cuid_unique": { + "name": "collection_items_cuid_unique", + "nullsNotDistinct": false, + "columns": [ + "cuid" + ] + } + } + }, + "public.collections": { + "name": "collections", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "cuid": { + "name": "cuid", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'My Collection'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "collections_user_id_users_id_fk": { + "name": "collections_user_id_users_id_fk", + "tableFrom": "collections", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "collections_cuid_unique": { + "name": "collections_cuid_unique", + "nullsNotDistinct": false, + "columns": [ + "cuid" + ] + } + } + }, + "public.credentials": { + "name": "credentials", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'password'" + }, + "secret_data": { + "name": "secret_data", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "credentials_user_id_users_id_fk": { + "name": "credentials_user_id_users_id_fk", + "tableFrom": "credentials", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.expansions": { + "name": "expansions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "cuid": { + "name": "cuid", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "base_game_id": { + "name": "base_game_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "game_id": { + "name": "game_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "expansions_base_game_id_games_id_fk": { + "name": "expansions_base_game_id_games_id_fk", + "tableFrom": "expansions", + "tableTo": "games", + "columnsFrom": [ + "base_game_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "cascade" + }, + "expansions_game_id_games_id_fk": { + "name": "expansions_game_id_games_id_fk", + "tableFrom": "expansions", + "tableTo": "games", + "columnsFrom": [ + "game_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "expansions_cuid_unique": { + "name": "expansions_cuid_unique", + "nullsNotDistinct": false, + "columns": [ + "cuid" + ] + } + } + }, + "public.external_ids": { + "name": "external_ids", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "cuid": { + "name": "cuid", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "type": { + "name": "type", + "type": "external_id_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "external_id": { + "name": "external_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "external_ids_cuid_unique": { + "name": "external_ids_cuid_unique", + "nullsNotDistinct": false, + "columns": [ + "cuid" + ] + } + } + }, + "public.federated_identity": { + "name": "federated_identity", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "identity_provider": { + "name": "identity_provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "federated_user_id": { + "name": "federated_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "federated_username": { + "name": "federated_username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "federated_identity_user_id_users_id_fk": { + "name": "federated_identity_user_id_users_id_fk", + "tableFrom": "federated_identity", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.games": { + "name": "games", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "cuid": { + "name": "cuid", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "year_published": { + "name": "year_published", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "min_players": { + "name": "min_players", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "max_players": { + "name": "max_players", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "playtime": { + "name": "playtime", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "min_playtime": { + "name": "min_playtime", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "max_playtime": { + "name": "max_playtime", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "min_age": { + "name": "min_age", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "image_url": { + "name": "image_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "thumb_url": { + "name": "thumb_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_sync_at": { + "name": "last_sync_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "search_index": { + "name": "search_index", + "columns": [ + { + "expression": "(\n\t\t\t\tsetweight(to_tsvector('english', \"name\"), 'A') ||\n setweight(to_tsvector('english', \"slug\"), 'B')\n )", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "games_cuid_unique": { + "name": "games_cuid_unique", + "nullsNotDistinct": false, + "columns": [ + "cuid" + ] + } + } + }, + "public.games_to_external_ids": { + "name": "games_to_external_ids", + "schema": "", + "columns": { + "game_id": { + "name": "game_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "external_id": { + "name": "external_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "games_to_external_ids_game_id_games_id_fk": { + "name": "games_to_external_ids_game_id_games_id_fk", + "tableFrom": "games_to_external_ids", + "tableTo": "games", + "columnsFrom": [ + "game_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "cascade" + }, + "games_to_external_ids_external_id_external_ids_id_fk": { + "name": "games_to_external_ids_external_id_external_ids_id_fk", + "tableFrom": "games_to_external_ids", + "tableTo": "external_ids", + "columnsFrom": [ + "external_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": { + "games_to_external_ids_game_id_external_id_pk": { + "name": "games_to_external_ids_game_id_external_id_pk", + "columns": [ + "game_id", + "external_id" + ] + } + }, + "uniqueConstraints": {} + }, + "public.mechanics": { + "name": "mechanics", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "cuid": { + "name": "cuid", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "mechanics_cuid_unique": { + "name": "mechanics_cuid_unique", + "nullsNotDistinct": false, + "columns": [ + "cuid" + ] + } + } + }, + "public.mechanics_to_external_ids": { + "name": "mechanics_to_external_ids", + "schema": "", + "columns": { + "mechanic_id": { + "name": "mechanic_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "external_id": { + "name": "external_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "mechanics_to_external_ids_mechanic_id_mechanics_id_fk": { + "name": "mechanics_to_external_ids_mechanic_id_mechanics_id_fk", + "tableFrom": "mechanics_to_external_ids", + "tableTo": "mechanics", + "columnsFrom": [ + "mechanic_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "cascade" + }, + "mechanics_to_external_ids_external_id_external_ids_id_fk": { + "name": "mechanics_to_external_ids_external_id_external_ids_id_fk", + "tableFrom": "mechanics_to_external_ids", + "tableTo": "external_ids", + "columnsFrom": [ + "external_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": { + "mechanics_to_external_ids_mechanic_id_external_id_pk": { + "name": "mechanics_to_external_ids_mechanic_id_external_id_pk", + "columns": [ + "mechanic_id", + "external_id" + ] + } + }, + "uniqueConstraints": {} + }, + "public.mechanics_to_games": { + "name": "mechanics_to_games", + "schema": "", + "columns": { + "mechanic_id": { + "name": "mechanic_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "game_id": { + "name": "game_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "mechanics_to_games_mechanic_id_mechanics_id_fk": { + "name": "mechanics_to_games_mechanic_id_mechanics_id_fk", + "tableFrom": "mechanics_to_games", + "tableTo": "mechanics", + "columnsFrom": [ + "mechanic_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "cascade" + }, + "mechanics_to_games_game_id_games_id_fk": { + "name": "mechanics_to_games_game_id_games_id_fk", + "tableFrom": "mechanics_to_games", + "tableTo": "games", + "columnsFrom": [ + "game_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": { + "mechanics_to_games_mechanic_id_game_id_pk": { + "name": "mechanics_to_games_mechanic_id_game_id_pk", + "columns": [ + "mechanic_id", + "game_id" + ] + } + }, + "uniqueConstraints": {} + }, + "public.password_reset_tokens": { + "name": "password_reset_tokens", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "password_reset_tokens_user_id_users_id_fk": { + "name": "password_reset_tokens_user_id_users_id_fk", + "tableFrom": "password_reset_tokens", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.publishers": { + "name": "publishers", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "cuid": { + "name": "cuid", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "publishers_cuid_unique": { + "name": "publishers_cuid_unique", + "nullsNotDistinct": false, + "columns": [ + "cuid" + ] + } + } + }, + "public.publishers_to_external_ids": { + "name": "publishers_to_external_ids", + "schema": "", + "columns": { + "publisher_id": { + "name": "publisher_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "external_id": { + "name": "external_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "publishers_to_external_ids_publisher_id_publishers_id_fk": { + "name": "publishers_to_external_ids_publisher_id_publishers_id_fk", + "tableFrom": "publishers_to_external_ids", + "tableTo": "publishers", + "columnsFrom": [ + "publisher_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "cascade" + }, + "publishers_to_external_ids_external_id_external_ids_id_fk": { + "name": "publishers_to_external_ids_external_id_external_ids_id_fk", + "tableFrom": "publishers_to_external_ids", + "tableTo": "external_ids", + "columnsFrom": [ + "external_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": { + "publishers_to_external_ids_publisher_id_external_id_pk": { + "name": "publishers_to_external_ids_publisher_id_external_id_pk", + "columns": [ + "publisher_id", + "external_id" + ] + } + }, + "uniqueConstraints": {} + }, + "public.publishers_to_games": { + "name": "publishers_to_games", + "schema": "", + "columns": { + "publisher_id": { + "name": "publisher_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "game_id": { + "name": "game_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "publishers_to_games_publisher_id_publishers_id_fk": { + "name": "publishers_to_games_publisher_id_publishers_id_fk", + "tableFrom": "publishers_to_games", + "tableTo": "publishers", + "columnsFrom": [ + "publisher_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "cascade" + }, + "publishers_to_games_game_id_games_id_fk": { + "name": "publishers_to_games_game_id_games_id_fk", + "tableFrom": "publishers_to_games", + "tableTo": "games", + "columnsFrom": [ + "game_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": { + "publishers_to_games_publisher_id_game_id_pk": { + "name": "publishers_to_games_publisher_id_game_id_pk", + "columns": [ + "publisher_id", + "game_id" + ] + } + }, + "uniqueConstraints": {} + }, + "public.recovery_codes": { + "name": "recovery_codes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "code": { + "name": "code", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "used": { + "name": "used", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "recovery_codes_user_id_users_id_fk": { + "name": "recovery_codes_user_id_users_id_fk", + "tableFrom": "recovery_codes", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.roles": { + "name": "roles", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "cuid": { + "name": "cuid", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "roles_cuid_unique": { + "name": "roles_cuid_unique", + "nullsNotDistinct": false, + "columns": [ + "cuid" + ] + }, + "roles_name_unique": { + "name": "roles_name_unique", + "nullsNotDistinct": false, + "columns": [ + "name" + ] + } + } + }, + "public.sessions": { + "name": "sessions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "ip_country": { + "name": "ip_country", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "ip_address": { + "name": "ip_address", + "type": "text", + "primaryKey": 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": {}, + "foreignKeys": { + "sessions_user_id_users_id_fk": { + "name": "sessions_user_id_users_id_fk", + "tableFrom": "sessions", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.two_factor": { + "name": "two_factor", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "cuid": { + "name": "cuid", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "secret": { + "name": "secret", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "initiated_time": { + "name": "initiated_time", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "two_factor_user_id_users_id_fk": { + "name": "two_factor_user_id_users_id_fk", + "tableFrom": "two_factor", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "two_factor_cuid_unique": { + "name": "two_factor_cuid_unique", + "nullsNotDistinct": false, + "columns": [ + "cuid" + ] + }, + "two_factor_user_id_unique": { + "name": "two_factor_user_id_unique", + "nullsNotDistinct": false, + "columns": [ + "user_id" + ] + } + } + }, + "public.user_roles": { + "name": "user_roles", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "cuid": { + "name": "cuid", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "role_id": { + "name": "role_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "primary": { + "name": "primary", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "user_roles_user_id_users_id_fk": { + "name": "user_roles_user_id_users_id_fk", + "tableFrom": "user_roles", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "user_roles_role_id_roles_id_fk": { + "name": "user_roles_role_id_roles_id_fk", + "tableFrom": "user_roles", + "tableTo": "roles", + "columnsFrom": [ + "role_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "user_roles_cuid_unique": { + "name": "user_roles_cuid_unique", + "nullsNotDistinct": false, + "columns": [ + "cuid" + ] + } + } + }, + "public.users": { + "name": "users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "cuid": { + "name": "cuid", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "first_name": { + "name": "first_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_name": { + "name": "last_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "verified": { + "name": "verified", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "receive_email": { + "name": "receive_email", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "mfa_enabled": { + "name": "mfa_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "theme": { + "name": "theme", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'system'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "users_cuid_unique": { + "name": "users_cuid_unique", + "nullsNotDistinct": false, + "columns": [ + "cuid" + ] + }, + "users_username_unique": { + "name": "users_username_unique", + "nullsNotDistinct": false, + "columns": [ + "username" + ] + }, + "users_email_unique": { + "name": "users_email_unique", + "nullsNotDistinct": false, + "columns": [ + "email" + ] + } + } + }, + "public.wishlist_items": { + "name": "wishlist_items", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "cuid": { + "name": "cuid", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "wishlist_id": { + "name": "wishlist_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "game_id": { + "name": "game_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "wishlist_items_wishlist_id_wishlists_id_fk": { + "name": "wishlist_items_wishlist_id_wishlists_id_fk", + "tableFrom": "wishlist_items", + "tableTo": "wishlists", + "columnsFrom": [ + "wishlist_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "wishlist_items_game_id_games_id_fk": { + "name": "wishlist_items_game_id_games_id_fk", + "tableFrom": "wishlist_items", + "tableTo": "games", + "columnsFrom": [ + "game_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "wishlist_items_cuid_unique": { + "name": "wishlist_items_cuid_unique", + "nullsNotDistinct": false, + "columns": [ + "cuid" + ] + } + } + }, + "public.wishlists": { + "name": "wishlists", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "cuid": { + "name": "cuid", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'My Wishlist'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "wishlists_user_id_users_id_fk": { + "name": "wishlists_user_id_users_id_fk", + "tableFrom": "wishlists", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "wishlists_cuid_unique": { + "name": "wishlists_cuid_unique", + "nullsNotDistinct": false, + "columns": [ + "cuid" + ] + } + } + } + }, + "enums": { + "public.external_id_type": { + "name": "external_id_type", + "schema": "public", + "values": [ + "game", + "category", + "mechanic", + "publisher", + "designer", + "artist" + ] + } + }, + "schemas": {}, + "sequences": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/src/lib/server/api/infrastructure/database/migrations/meta/_journal.json b/src/lib/server/api/infrastructure/database/migrations/meta/_journal.json index c0c8782..3fa0832 100644 --- a/src/lib/server/api/infrastructure/database/migrations/meta/_journal.json +++ b/src/lib/server/api/infrastructure/database/migrations/meta/_journal.json @@ -29,6 +29,20 @@ "when": 1723593488634, "tag": "0003_worried_taskmaster", "breakpoints": true + }, + { + "idx": 4, + "version": "7", + "when": 1725055403926, + "tag": "0004_heavy_sphinx", + "breakpoints": true + }, + { + "idx": 5, + "version": "7", + "when": 1725055643756, + "tag": "0005_true_mathemanic", + "breakpoints": true } ] } \ No newline at end of file diff --git a/src/lib/server/api/infrastructure/database/tables/users.table.ts b/src/lib/server/api/infrastructure/database/tables/users.table.ts index 83f6fef..a61ca90 100644 --- a/src/lib/server/api/infrastructure/database/tables/users.table.ts +++ b/src/lib/server/api/infrastructure/database/tables/users.table.ts @@ -1,8 +1,8 @@ -import { boolean, pgTable, text, uuid } from 'drizzle-orm/pg-core'; -import { type InferSelectModel, relations } from 'drizzle-orm'; -import { createId as cuid2 } from '@paralleldrive/cuid2'; -import { timestamps } from '../utils'; -import {user_roles} from './userRoles'; +import { boolean, pgTable, text, uuid } from 'drizzle-orm/pg-core' +import { type InferSelectModel, relations } from 'drizzle-orm' +import { createId as cuid2 } from '@paralleldrive/cuid2' +import { timestamps } from '../utils' +import { user_roles } from './userRoles' export const usersTable = pgTable('users', { id: uuid('id').primaryKey().defaultRandom(), @@ -15,12 +15,13 @@ export const usersTable = pgTable('users', { last_name: text('last_name'), verified: boolean('verified').default(false), receive_email: boolean('receive_email').default(false), + mfa_enabled: boolean('mfa_enabled').notNull().default(false), theme: text('theme').default('system'), ...timestamps, -}); +}) export const userRelations = relations(usersTable, ({ many }) => ({ user_roles: many(user_roles), -})); +})) -export type Users = InferSelectModel; +export type Users = InferSelectModel diff --git a/src/lib/server/api/middleware/auth.middleware.ts b/src/lib/server/api/middleware/auth.middleware.ts index 9c21ea8..bd2f4c9 100644 --- a/src/lib/server/api/middleware/auth.middleware.ts +++ b/src/lib/server/api/middleware/auth.middleware.ts @@ -1,50 +1,50 @@ -import type { MiddlewareHandler } from 'hono'; -import { createMiddleware } from 'hono/factory'; -import type { HonoTypes } from '../types'; -import { lucia } from '../infrastructure/auth/lucia'; -import { verifyRequestOrigin } from 'lucia'; -import type { Session, User } from 'lucia'; -import { Unauthorized } from '../common/errors'; +import type { MiddlewareHandler } from 'hono' +import { createMiddleware } from 'hono/factory' +import type { HonoTypes } from '../types' +import { lucia } from '../infrastructure/auth/lucia' +import { verifyRequestOrigin } from 'oslo/request' +import type { Session, User } from 'lucia' +import { Unauthorized } from '../common/errors' export const verifyOrigin: MiddlewareHandler = createMiddleware(async (c, next) => { - if (c.req.method === "GET") { - return next(); + if (c.req.method === 'GET') { + return next() } - const originHeader = c.req.header("Origin") ?? null; - const hostHeader = c.req.header("Host") ?? null; + const originHeader = c.req.header('Origin') ?? null + const hostHeader = c.req.header('Host') ?? null if (!originHeader || !hostHeader || !verifyRequestOrigin(originHeader, [hostHeader])) { - return c.body(null, 403); + return c.body(null, 403) } - return next(); + return next() }) export const validateAuthSession: MiddlewareHandler = createMiddleware(async (c, next) => { - const sessionId = lucia.readSessionCookie(c.req.header("Cookie") ?? ""); + const sessionId = lucia.readSessionCookie(c.req.header('Cookie') ?? '') if (!sessionId) { - c.set("user", null); - c.set("session", null); - return next(); + c.set('user', null) + c.set('session', null) + return next() } - const { session, user } = await lucia.validateSession(sessionId); + const { session, user } = await lucia.validateSession(sessionId) if (session && session.fresh) { - c.header("Set-Cookie", lucia.createSessionCookie(session.id).serialize(), { append: true }); + c.header('Set-Cookie', lucia.createSessionCookie(session.id).serialize(), { append: true }) } if (!session) { - c.header("Set-Cookie", lucia.createBlankSessionCookie().serialize(), { append: true }); + c.header('Set-Cookie', lucia.createBlankSessionCookie().serialize(), { append: true }) } - c.set("session", session); - c.set("user", user); - return next(); + c.set('session', session) + c.set('user', user) + return next() }) export const requireAuth: MiddlewareHandler<{ Variables: { - session: Session; - user: User; - }; + session: Session + user: User + } }> = createMiddleware(async (c, next) => { - const user = c.var.user; - if (!user) throw Unauthorized('You must be logged in to access this resource'); - return next(); -}); + const user = c.var.user + if (!user) throw Unauthorized('You must be logged in to access this resource') + return next() +}) diff --git a/src/lib/server/api/repositories/credentials.repository.ts b/src/lib/server/api/repositories/credentials.repository.ts index 84d3334..2594af5 100644 --- a/src/lib/server/api/repositories/credentials.repository.ts +++ b/src/lib/server/api/repositories/credentials.repository.ts @@ -1,83 +1,69 @@ -import { and, eq, type InferInsertModel } from "drizzle-orm"; -import { credentialsTable, CredentialsType } from "../infrastructure/database/tables/credentials.table"; -import { takeFirstOrThrow } from "../infrastructure/database/utils"; -import {inject, injectable} from "tsyringe"; -import {DatabaseProvider} from "$lib/server/api/providers"; +import { and, eq, type InferInsertModel } from 'drizzle-orm' +import { credentialsTable, CredentialsType } from '../infrastructure/database/tables/credentials.table' +import { takeFirstOrThrow } from '../infrastructure/database/utils' +import { inject, injectable } from 'tsyringe' +import { DatabaseProvider } from '$lib/server/api/providers' -export type CreateCredentials = InferInsertModel; -export type UpdateCredentials = Partial; +export type CreateCredentials = InferInsertModel +export type UpdateCredentials = Partial @injectable() export class CredentialsRepository { - constructor(@inject(DatabaseProvider) private readonly db: DatabaseProvider) { } + constructor(@inject(DatabaseProvider) private readonly db: DatabaseProvider) {} async findOneByUserId(userId: string) { return this.db.query.credentialsTable.findFirst({ - where: eq(credentialsTable.user_id, userId) - }); + where: eq(credentialsTable.user_id, userId), + }) } async findOneByUserIdAndType(userId: string, type: CredentialsType) { return this.db.query.credentialsTable.findFirst({ - where: and( - eq(credentialsTable.user_id, userId), - eq(credentialsTable.type, type) - ) - }); + where: and(eq(credentialsTable.user_id, userId), eq(credentialsTable.type, type)), + }) } async findPasswordCredentialsByUserId(userId: string) { return this.db.query.credentialsTable.findFirst({ - where: and( - eq(credentialsTable.user_id, userId), - eq(credentialsTable.type, CredentialsType.PASSWORD) - ) - }); + where: and(eq(credentialsTable.user_id, userId), eq(credentialsTable.type, CredentialsType.PASSWORD)), + }) } async findTOTPCredentialsByUserId(userId: string) { return this.db.query.credentialsTable.findFirst({ - where: and( - eq(credentialsTable.user_id, userId), - eq(credentialsTable.type, CredentialsType.TOTP) - ) - }); + where: and(eq(credentialsTable.user_id, userId), eq(credentialsTable.type, CredentialsType.TOTP)), + }) } async findOneById(id: string) { return this.db.query.credentialsTable.findFirst({ - where: eq(credentialsTable.id, id) - }); + where: eq(credentialsTable.id, id), + }) } async findOneByIdOrThrow(id: string) { - const credentials = await this.findOneById(id); - if (!credentials) throw Error('Credentials not found'); - return credentials; + const credentials = await this.findOneById(id) + if (!credentials) throw Error('Credentials not found') + return credentials } async create(data: CreateCredentials) { - return this.db.insert(credentialsTable).values(data).returning().then(takeFirstOrThrow); + return this.db.insert(credentialsTable).values(data).returning().then(takeFirstOrThrow) } async update(id: string, data: UpdateCredentials) { - return this.db - .update(credentialsTable) - .set(data) - .where(eq(credentialsTable.id, id)) - .returning() - .then(takeFirstOrThrow); + return this.db.update(credentialsTable).set(data).where(eq(credentialsTable.id, id)).returning().then(takeFirstOrThrow) } async delete(id: string) { - return this.db - .delete(credentialsTable) - .where(eq(credentialsTable.id, id)); + return this.db.delete(credentialsTable).where(eq(credentialsTable.id, id)) } async deleteByUserId(userId: string) { - return this.db - .delete(credentialsTable) - .where(eq(credentialsTable.user_id, userId)); + return this.db.delete(credentialsTable).where(eq(credentialsTable.user_id, userId)) } -} \ No newline at end of file + + async deleteByUserIdAndType(userId: string, type: CredentialsType) { + return this.db.delete(credentialsTable).where(and(eq(credentialsTable.user_id, userId), eq(credentialsTable.type, type))) + } +} diff --git a/src/lib/server/api/services/totp.service.ts b/src/lib/server/api/services/totp.service.ts index 77bb004..f75674e 100644 --- a/src/lib/server/api/services/totp.service.ts +++ b/src/lib/server/api/services/totp.service.ts @@ -1,7 +1,9 @@ import { inject, injectable } from "tsyringe"; import { HMAC } from 'oslo/crypto'; -import { encodeHex } from 'oslo/encoding'; +import { decodeHex, encodeHex } from 'oslo/encoding'; import {CredentialsRepository} from "$lib/server/api/repositories/credentials.repository"; +import { TOTPController } from "oslo/otp"; +import type { CredentialsType } from "$db/tables"; @injectable() export class TotpService { @@ -15,6 +17,14 @@ export class TotpService { return this.credentialsRepository.findTOTPCredentialsByUserId(userId); } + async findOneByUserIdOrThrow(userId: string) { + const credential = await this.findOneByUserId(userId); + if (!credential) { + throw new Error('TOTP credential not found'); + } + return credential; + } + async create(userId: string) { const twoFactorSecret = await new HMAC('SHA-1').generateKey(); @@ -33,4 +43,16 @@ export class TotpService { async deleteOneByUserId(userId: string) { return this.credentialsRepository.deleteByUserId(userId); } + + async deleteOneByUserIdAndType(userId: string, type: CredentialsType) { + return this.credentialsRepository.deleteByUserIdAndType(userId, type) + } + + async verify(userId: string, code: string) { + const credential = await this.credentialsRepository.findTOTPCredentialsByUserId(userId); + if (!credential) { + throw new Error('TOTP credential not found'); + } + return await new TOTPController().verify(code, decodeHex(credential.secret_data)) + } } \ No newline at end of file diff --git a/src/lib/utils/api.ts b/src/lib/utils/api.ts index 12fb9c5..38dfdc4 100644 --- a/src/lib/utils/api.ts +++ b/src/lib/utils/api.ts @@ -10,7 +10,7 @@ export async function parseApiResponse(response: ClientResponse) { if (response.ok) { const data = await response.json() as T; - return { data, error: null, status: response.status }; + return { data, error: null, response } } // handle errors diff --git a/src/routes/(app)/(protected)/profile/security/mfa/+page.server.ts b/src/routes/(app)/(protected)/profile/security/mfa/+page.server.ts index 64a2394..cbb2dc9 100644 --- a/src/routes/(app)/(protected)/profile/security/mfa/+page.server.ts +++ b/src/routes/(app)/(protected)/profile/security/mfa/+page.server.ts @@ -1,43 +1,43 @@ -import { type Actions, fail, error } from '@sveltejs/kit'; -import { eq } from 'drizzle-orm'; -import { encodeHex, decodeHex } from 'oslo/encoding'; -import { Argon2id } from 'oslo/password'; -import { createTOTPKeyURI, TOTPController } from 'oslo/otp'; -import { HMAC } from 'oslo/crypto'; -import kebabCase from 'just-kebab-case'; -import QRCode from 'qrcode'; -import { zod } from 'sveltekit-superforms/adapters'; -import { setError, superValidate } from 'sveltekit-superforms/server'; -import { redirect, setFlash } from 'sveltekit-flash-message/server'; -import type { PageServerLoad } from '../../$types'; -import { addTwoFactorSchema, removeTwoFactorSchema } from '$lib/validations/account'; -import { notSignedInMessage } from '$lib/flashMessages'; -import { db } from '$lib/server/api/infrastructure/database'; -import { recoveryCodesTable, credentialsTable, usersTable, type Credentials } from '$lib/server/api/infrastructure/database/tables'; -import { userNotAuthenticated } from '$lib/server/auth-utils'; -import env from '$src/env'; +import { type Actions, fail, error } from '@sveltejs/kit' +import { eq } from 'drizzle-orm' +import { encodeHex, decodeHex } from 'oslo/encoding' +import { Argon2id } from 'oslo/password' +import { createTOTPKeyURI, TOTPController } from 'oslo/otp' +import { HMAC } from 'oslo/crypto' +import kebabCase from 'just-kebab-case' +import QRCode from 'qrcode' +import { zod } from 'sveltekit-superforms/adapters' +import { setError, superValidate } from 'sveltekit-superforms/server' +import { redirect, setFlash } from 'sveltekit-flash-message/server' +import type { PageServerLoad } from '../../$types' +import { addTwoFactorSchema, removeTwoFactorSchema } from '$lib/validations/account' +import { notSignedInMessage } from '$lib/flashMessages' +import { db } from '$lib/server/api/infrastructure/database' +import { recoveryCodesTable, credentialsTable, usersTable, type Credentials } from '$lib/server/api/infrastructure/database/tables' +import { userNotAuthenticated } from '$lib/server/auth-utils' +import env from '$src/env' +import { StatusCodes } from '$lib/constants/status-codes' export const load: PageServerLoad = async (event) => { - const { locals } = event; + const { locals } = event - const authedUser = await locals.getAuthedUser(); + const authedUser = await locals.getAuthedUser() if (!authedUser) { - throw redirect(302, '/login', notSignedInMessage, event); + throw redirect(302, '/login', notSignedInMessage, event) } - const addTwoFactorForm = await superValidate(event, zod(addTwoFactorSchema)); - const removeTwoFactorForm = await superValidate(event, zod(removeTwoFactorSchema)); + const addTwoFactorForm = await superValidate(event, zod(addTwoFactorSchema)) + const removeTwoFactorForm = await superValidate(event, zod(removeTwoFactorSchema)) // const addAuthNFactorForm = await superValidate(event, zod(addAuthNFactorSchema)); - const { data, error } = await locals.api.mfa.totp.$get().then(locals.parseApiResponse); + const { data, error } = await locals.api.mfa.totp.$get().then(locals.parseApiResponse) if (error || !data) { return fail(500, { addTwoFactorForm, - }); + }) } const { totpCredential } = data - - if (totpCredential) { + if (totpCredential && authedUser.mfa_enabled) { return { addTwoFactorForm, removeTwoFactorForm, @@ -48,9 +48,13 @@ export const load: PageServerLoad = async (event) => { } } - const issuer = kebabCase(env.PUBLIC_SITE_NAME); - const accountName = authedUser.email || authedUser.username; - const { data: createdTotpData, error: createdTotpError } = await locals.api.mfa.totp.$post().then(locals.parseApiResponse); + if (totpCredential && !authedUser.mfa_enabled) { + await locals.api.mfa.totp.$delete().then(locals.parseApiResponse) + } + + const issuer = kebabCase(env.PUBLIC_SITE_NAME) + const accountName = authedUser.email || authedUser.username + const { data: createdTotpData, error: createdTotpError } = await locals.api.mfa.totp.$post().then(locals.parseApiResponse) if (createdTotpError || !createdTotpData) { return fail(500, { @@ -58,19 +62,19 @@ export const load: PageServerLoad = async (event) => { }) } - const { totpCredential: createdTotpCredentials } = createdTotpData; + const { totpCredential: createdTotpCredentials } = createdTotpData // pass the website's name and the user identifier (e.g. email, username) if (!createdTotpCredentials?.secret_data) { return fail(500, { addTwoFactorForm, }) } - const totpUri = createTOTPKeyURI(issuer, accountName, createdTotpCredentials.secret_data); + const totpUri = createTOTPKeyURI(issuer, accountName, decodeHex(createdTotpCredentials.secret_data)) addTwoFactorForm.data = { current_password: '', two_factor_code: '', - }; + } return { addTwoFactorForm, removeTwoFactorForm, @@ -78,8 +82,8 @@ export const load: PageServerLoad = async (event) => { recoveryCodes: [], totpUri, qrCode: await QRCode.toDataURL(totpUri), - }; -}; + } +} export const actions: Actions = { enableTotp: async (event) => { @@ -98,31 +102,12 @@ export const actions: Actions = { }) } - const { data, error } = await locals.api.mfa.totp.$get().then(locals.parseApiResponse) - if (error || !data) { - return fail(500, { - addTwoFactorForm, - }) - } - const { totpCredential } = data - - if (!totpCredential) { - addTwoFactorForm.data.current_password = '' - addTwoFactorForm.data.two_factor_code = '' - return setError(addTwoFactorForm, 'Error occurred. Please try again or contact support if you need further help.') - } - - if (totpCredential.secret_data === '' || totpCredential.secret_data === null) { - addTwoFactorForm.data.current_password = '' - addTwoFactorForm.data.two_factor_code = '' - return setError(addTwoFactorForm, 'Error occurred. Please try again or contact support if you need further help.') - } - - const currentPasswordVerified = await locals.api.me.verify.password.$post({ + const { error: verifyPasswordError } = await locals.api.me.verify.password.$post({ json: { password: addTwoFactorForm.data.current_password }, - }); + }).then(locals.parseApiResponse) - if (!currentPasswordVerified) { + if (verifyPasswordError) { + console.log(verifyPasswordError) return setError(addTwoFactorForm, 'current_password', 'Your password is incorrect') } @@ -131,14 +116,16 @@ export const actions: Actions = { } const twoFactorCode = addTwoFactorForm.data.two_factor_code - const validOTP = await new TOTPController().verify(twoFactorCode, decodeHex(twoFactorDetails.secret)) + const { error: verifyTotpError } = locals.api.mfa.totp.verify + .$post({ + json: { code: twoFactorCode }, + }) + .then(locals.parseApiResponse) - if (!validOTP) { + if (verifyTotpError) { return setError(addTwoFactorForm, 'two_factor_code', 'Invalid code') } - await db.update(twoFactor).set({ enabled: true }).where(eq(twoFactor.userId, user!.id!)) - redirect(302, '/profile/security/two-factor/recovery-codes') }, disableTotp: async (event) => { diff --git a/src/routes/(app)/(protected)/profile/security/mfa/recovery-codes/+page.server.ts b/src/routes/(app)/(protected)/profile/security/mfa/recovery-codes/+page.server.ts index b14c28e..84ceece 100644 --- a/src/routes/(app)/(protected)/profile/security/mfa/recovery-codes/+page.server.ts +++ b/src/routes/(app)/(protected)/profile/security/mfa/recovery-codes/+page.server.ts @@ -5,8 +5,7 @@ import { redirect } from 'sveltekit-flash-message/server'; import { db } from '$lib/server/api/infrastructure/database'; import { notSignedInMessage } from '$lib/flashMessages'; import type { PageServerLoad } from '../../../$types'; -import { recoveryCodesTable, twoFactorTable, usersTable} from '$lib/server/api/infrastructure/database/tables'; -import { userNotAuthenticated } from '$lib/server/auth-utils'; +import { recoveryCodesTable } from '$lib/server/api/infrastructure/database/tables'; export const load: PageServerLoad = async (event) => { const { locals } = event; @@ -16,51 +15,31 @@ export const load: PageServerLoad = async (event) => { throw redirect(302, '/login', notSignedInMessage, event); } - const dbUser = await db.query.usersTable.findFirst({ - where: eq(usersTable.id, authedUser.id), - }); - - if (!dbUser) { - throw redirect(302, '/login', notSignedInMessage, event); - } - - const twoFactorDetails = await db.query.twoFactor.findFirst({ - where: eq(twoFactor.userId, dbUser.id), - }); - - if (twoFactorDetails?.enabled) { - const dbRecoveryCodes = await db.query.recoveryCodes.findMany({ - where: eq(recoveryCodes.userId, authedUser.id), - }); + if (authedUser.mfa_enabled) { + const dbRecoveryCodes = await db.query.recoveryCodesTable.findMany({ + where: eq(recoveryCodesTable.userId, authedUser.id), + }) if (dbRecoveryCodes.length === 0) { - const createdRecoveryCodes = Array.from({ length: 5 }, () => - generateRandomString(10, alphabet('A-Z', '0-9')), - ); + const createdRecoveryCodes = Array.from({ length: 5 }, () => generateRandomString(10, alphabet('A-Z', '0-9'))) if (createdRecoveryCodes) { for (const code of createdRecoveryCodes) { - const hashedCode = await new Argon2id().hash(code); - console.log('Inserting recovery code', code, hashedCode); - await db.insert(recoveryCodes).values({ + const hashedCode = await new Argon2id().hash(code) + console.log('Inserting recovery code', code, hashedCode) + await db.insert(recoveryCodesTable).values({ userId: authedUser.id, code: hashedCode, - }); + }) } } return { recoveryCodes: createdRecoveryCodes, - }; + } } return { recoveryCodes: [], - }; - } else { - console.error('2FA not enabled'); - redirect( - 302, - '/profile', - { message: 'Two-Factor Authentication is not enabled', type: 'error' }, - event, - ); + } } + console.error('2FA not enabled') + redirect(302, '/profile', { message: 'Two-Factor Authentication is not enabled', type: 'error' }, event) };