From 3993596986206cc1456074edc523c15647dfcd15 Mon Sep 17 00:00:00 2001 From: Bradley Shellnut Date: Wed, 6 Nov 2024 09:49:18 -0800 Subject: [PATCH] Formatting files, upgrading Drizzle, using snake case in Drizzle, update tables, fix unique ID with new Drizzle. --- drizzle.config.ts | 13 +- oldApis/reset-password/+server.ts | 28 +- oldApis/reset-password/[token]/+server.ts | 24 +- package.json | 58 +- pnpm-lock.yaml | 652 +++--- src/lib/server/api/common/config.ts | 2 + src/lib/server/api/common/types/config.ts | 30 +- src/lib/server/api/common/utils/cookies.ts | 34 + src/lib/server/api/common/utils/table.ts | 17 +- .../api/controllers/collection.routes.ts | 2 +- .../server/api/controllers/iam.controller.ts | 29 +- src/lib/server/api/controllers/iam.routes.ts | 2 +- .../api/controllers/login.controller.ts | 70 +- .../server/api/controllers/mfa.controller.ts | 82 +- .../api/controllers/oauth.controller.ts | 117 +- .../api/controllers/signup.controller.ts | 48 +- src/lib/server/api/databases/migrate.ts | 30 - .../databases/migrations/meta/_journal.json | 20 - .../server/api/databases/postgres/migrate.ts | 33 + .../migrations/0000_volatile_warhawk.sql | 0 .../migrations/0001_pink_the_enforcers.sql | 0 .../migrations/0002_mysterious_justice.sql | 7 + .../postgres/migrations/0003_far_siren.sql | 2 + .../migrations/0004_lush_virginia_dare.sql | 2 + .../migrations/meta/0000_snapshot.json | 0 .../migrations/meta/0001_snapshot.json | 0 .../migrations/meta/0002_snapshot.json | 1876 ++++++++++++++++ .../migrations/meta/0003_snapshot.json | 1957 +++++++++++++++++ .../migrations/meta/0004_snapshot.json | 1957 +++++++++++++++++ .../postgres/migrations/meta/_journal.json | 41 + .../schemas/collections.schema.ts | 14 +- .../{ => postgres}/schemas/users.schemas.ts | 14 +- .../api/databases/{ => postgres}/seed.ts | 30 +- .../{ => postgres}/seeds/data/roles.json | 0 .../{ => postgres}/seeds/data/users.json | 0 .../databases/{ => postgres}/seeds/index.ts | 0 .../api/databases/postgres/seeds/roles.ts | 11 + .../databases/{ => postgres}/seeds/users.ts | 4 +- .../{ => postgres}/tables/categories.table.ts | 26 +- .../tables/categoriesToExternalIds.table.ts | 18 +- .../tables/categoriesToGames.table.ts | 18 +- .../tables/collectionItems.table.ts | 28 +- .../tables/collections.table.ts | 10 +- .../postgres/tables/credentials.table.ts | 23 + .../{ => postgres}/tables/expansions.table.ts | 24 +- .../databases/postgres/tables/expansions.ts | 32 + .../postgres/tables/externalIds.table.ts | 16 + .../tables/federatedIdentity.table.ts | 17 + .../databases/postgres/tables/games.table.ts | 51 + .../tables/gamesToExternalIds.table.ts | 18 +- .../databases/{ => postgres}/tables/index.ts | 0 .../postgres/tables/mechanics.table.ts | 23 + .../databases/postgres/tables/mechanics.ts | 23 + .../tables/mechanicsToExternalIds.table.ts | 0 .../tables/mechanicsToGames.table.ts | 18 +- .../tables/passwordResetTokens.table.ts | 22 +- .../{ => postgres}/tables/publishers.table.ts | 26 +- .../databases/postgres/tables/publishers.ts | 23 + .../tables/publishersToExternalIds.table.ts | 18 +- .../tables/publishersToGames.table.ts | 18 +- .../postgres/tables/recovery-codes.table.ts | 16 + .../databases/postgres/tables/roles.table.ts | 28 + .../{ => postgres}/tables/sessions.table.ts | 21 +- .../postgres/tables/two-factor.table.ts | 32 + .../{ => postgres}/tables/userRoles.table.ts | 28 +- .../{ => postgres}/tables/users.table.ts | 26 +- .../tables/wishlistItems.table.ts | 26 +- .../postgres/tables/wishlists.table.ts | 26 + src/lib/server/api/databases/seeds/roles.ts | 11 - .../api/databases/tables/credentials.table.ts | 23 - .../server/api/databases/tables/expansions.ts | 32 - .../api/databases/tables/externalIds.table.ts | 16 - .../tables/federatedIdentity.table.ts | 17 - .../api/databases/tables/games.table.ts | 51 - .../api/databases/tables/mechanics.table.ts | 23 - .../server/api/databases/tables/mechanics.ts | 23 - .../server/api/databases/tables/publishers.ts | 23 - .../databases/tables/recovery-codes.table.ts | 16 - .../api/databases/tables/roles.table.ts | 28 - .../api/databases/tables/two-factor.table.ts | 32 - .../api/databases/tables/wishlists.table.ts | 26 - .../server/api/middleware/auth.middleware.ts | 7 +- src/lib/server/api/packages/drizzle.ts | 36 +- .../repositories/collections.repository.ts | 30 +- .../repositories/credentials.repository.ts | 44 +- .../federated_identity.repository.ts | 18 +- .../repositories/recovery-codes.repository.ts | 20 +- .../api/repositories/roles.repository.ts | 38 +- .../api/repositories/sessions.repository.ts | 33 + .../api/repositories/user_roles.repository.ts | 28 +- .../api/repositories/users.repository.ts | 2 +- .../api/repositories/wishlists.repository.ts | 28 +- .../server/api/services/drizzle.service.ts | 2 +- src/lib/server/api/services/iam.service.ts | 50 +- .../api/services/loginrequest.service.ts | 66 +- src/lib/server/api/services/lucia.service.ts | 45 - .../server/api/services/sessions.service.ts | 71 + src/lib/server/api/services/totp.service.ts | 2 +- src/lib/server/api/services/users.service.ts | 92 +- src/lib/server/api/tests/iam.service.test.ts | 126 +- .../api/tests/user_roles.service.test.ts | 76 +- .../server/api/tests/users.service.test.ts | 160 +- src/lib/server/auth-utils.ts | 24 +- src/lib/types.ts | 272 +-- src/lib/utils/db/categoryUtils.ts | 52 +- src/lib/utils/db/expansionUtils.ts | 34 +- src/lib/utils/db/gameUtils.ts | 128 +- src/lib/utils/db/mechanicUtils.ts | 46 +- src/lib/utils/db/publisherUtils.ts | 62 +- .../(app)/(protected)/admin/+layout.server.ts | 30 +- .../admin/users/[id]/+page.server.ts | 96 +- .../(protected)/collections/+page.server.ts | 100 +- .../collections/[cuid]/+page.server.ts | 116 +- .../(app)/(protected)/list/+layout.server.ts | 24 +- .../(protected)/list/[id]/+page.server.ts | 82 +- .../settings/profile/+page.server.ts | 2 +- .../(protected)/wishlists/+page.server.ts | 108 +- .../wishlists/[cuid]/+page.server.ts | 118 +- src/routes/(app)/game/[id]/+page.server.ts | 111 +- src/routes/(auth)/totp/+page.server.ts | 120 +- 120 files changed, 8343 insertions(+), 2338 deletions(-) create mode 100644 src/lib/server/api/common/utils/cookies.ts delete mode 100644 src/lib/server/api/databases/migrate.ts delete mode 100644 src/lib/server/api/databases/migrations/meta/_journal.json create mode 100644 src/lib/server/api/databases/postgres/migrate.ts rename src/lib/server/api/databases/{ => postgres}/migrations/0000_volatile_warhawk.sql (100%) rename src/lib/server/api/databases/{ => postgres}/migrations/0001_pink_the_enforcers.sql (100%) create mode 100644 src/lib/server/api/databases/postgres/migrations/0002_mysterious_justice.sql create mode 100644 src/lib/server/api/databases/postgres/migrations/0003_far_siren.sql create mode 100644 src/lib/server/api/databases/postgres/migrations/0004_lush_virginia_dare.sql rename src/lib/server/api/databases/{ => postgres}/migrations/meta/0000_snapshot.json (100%) rename src/lib/server/api/databases/{ => postgres}/migrations/meta/0001_snapshot.json (100%) create mode 100644 src/lib/server/api/databases/postgres/migrations/meta/0002_snapshot.json create mode 100644 src/lib/server/api/databases/postgres/migrations/meta/0003_snapshot.json create mode 100644 src/lib/server/api/databases/postgres/migrations/meta/0004_snapshot.json create mode 100644 src/lib/server/api/databases/postgres/migrations/meta/_journal.json rename src/lib/server/api/databases/{ => postgres}/schemas/collections.schema.ts (82%) rename src/lib/server/api/databases/{ => postgres}/schemas/users.schemas.ts (71%) rename src/lib/server/api/databases/{ => postgres}/seed.ts (64%) rename src/lib/server/api/databases/{ => postgres}/seeds/data/roles.json (100%) rename src/lib/server/api/databases/{ => postgres}/seeds/data/users.json (100%) rename src/lib/server/api/databases/{ => postgres}/seeds/index.ts (100%) create mode 100644 src/lib/server/api/databases/postgres/seeds/roles.ts rename src/lib/server/api/databases/{ => postgres}/seeds/users.ts (95%) rename src/lib/server/api/databases/{ => postgres}/tables/categories.table.ts (57%) rename src/lib/server/api/databases/{ => postgres}/tables/categoriesToExternalIds.table.ts (73%) rename src/lib/server/api/databases/{ => postgres}/tables/categoriesToGames.table.ts (72%) rename src/lib/server/api/databases/{ => postgres}/tables/collectionItems.table.ts (59%) rename src/lib/server/api/databases/{ => postgres}/tables/collections.table.ts (81%) create mode 100644 src/lib/server/api/databases/postgres/tables/credentials.table.ts rename src/lib/server/api/databases/{ => postgres}/tables/expansions.table.ts (61%) create mode 100644 src/lib/server/api/databases/postgres/tables/expansions.ts create mode 100644 src/lib/server/api/databases/postgres/tables/externalIds.table.ts create mode 100644 src/lib/server/api/databases/postgres/tables/federatedIdentity.table.ts create mode 100644 src/lib/server/api/databases/postgres/tables/games.table.ts rename src/lib/server/api/databases/{ => postgres}/tables/gamesToExternalIds.table.ts (72%) rename src/lib/server/api/databases/{ => postgres}/tables/index.ts (100%) create mode 100644 src/lib/server/api/databases/postgres/tables/mechanics.table.ts create mode 100644 src/lib/server/api/databases/postgres/tables/mechanics.ts rename src/lib/server/api/databases/{ => postgres}/tables/mechanicsToExternalIds.table.ts (100%) rename src/lib/server/api/databases/{ => postgres}/tables/mechanicsToGames.table.ts (72%) rename src/lib/server/api/databases/{ => postgres}/tables/passwordResetTokens.table.ts (61%) rename src/lib/server/api/databases/{ => postgres}/tables/publishers.table.ts (50%) create mode 100644 src/lib/server/api/databases/postgres/tables/publishers.ts rename src/lib/server/api/databases/{ => postgres}/tables/publishersToExternalIds.table.ts (73%) rename src/lib/server/api/databases/{ => postgres}/tables/publishersToGames.table.ts (72%) create mode 100644 src/lib/server/api/databases/postgres/tables/recovery-codes.table.ts create mode 100644 src/lib/server/api/databases/postgres/tables/roles.table.ts rename src/lib/server/api/databases/{ => postgres}/tables/sessions.table.ts (53%) create mode 100644 src/lib/server/api/databases/postgres/tables/two-factor.table.ts rename src/lib/server/api/databases/{ => postgres}/tables/userRoles.table.ts (59%) rename src/lib/server/api/databases/{ => postgres}/tables/users.table.ts (53%) rename src/lib/server/api/databases/{ => postgres}/tables/wishlistItems.table.ts (57%) create mode 100644 src/lib/server/api/databases/postgres/tables/wishlists.table.ts delete mode 100644 src/lib/server/api/databases/seeds/roles.ts delete mode 100644 src/lib/server/api/databases/tables/credentials.table.ts delete mode 100644 src/lib/server/api/databases/tables/expansions.ts delete mode 100644 src/lib/server/api/databases/tables/externalIds.table.ts delete mode 100644 src/lib/server/api/databases/tables/federatedIdentity.table.ts delete mode 100644 src/lib/server/api/databases/tables/games.table.ts delete mode 100644 src/lib/server/api/databases/tables/mechanics.table.ts delete mode 100644 src/lib/server/api/databases/tables/mechanics.ts delete mode 100644 src/lib/server/api/databases/tables/publishers.ts delete mode 100644 src/lib/server/api/databases/tables/recovery-codes.table.ts delete mode 100644 src/lib/server/api/databases/tables/roles.table.ts delete mode 100644 src/lib/server/api/databases/tables/two-factor.table.ts delete mode 100644 src/lib/server/api/databases/tables/wishlists.table.ts create mode 100644 src/lib/server/api/repositories/sessions.repository.ts delete mode 100644 src/lib/server/api/services/lucia.service.ts create mode 100644 src/lib/server/api/services/sessions.service.ts diff --git a/drizzle.config.ts b/drizzle.config.ts index f5f2c7e..99372fc 100644 --- a/drizzle.config.ts +++ b/drizzle.config.ts @@ -1,11 +1,12 @@ -import 'dotenv/config' -import env from './src/lib/server/api/common/env' -import { defineConfig } from 'drizzle-kit' +import 'dotenv/config'; +import { defineConfig } from 'drizzle-kit'; +import env from './src/lib/server/api/common/env'; export default defineConfig({ dialect: 'postgresql', - out: './src/lib/server/api/databases/migrations', - schema: './src/lib/server/api/databases/tables/index.ts', + out: './src/lib/server/api/databases/postgres/migrations', + schema: './src/lib/server/api/databases/postgres/tables/index.ts', + casing: 'snake_case', dbCredentials: { host: env.DATABASE_HOST || 'localhost', port: Number(env.DATABASE_PORT) || 5432, @@ -22,4 +23,4 @@ export default defineConfig({ table: 'migrations', schema: 'public', }, -}) +}); diff --git a/oldApis/reset-password/+server.ts b/oldApis/reset-password/+server.ts index 3187ce2..1f1be77 100644 --- a/oldApis/reset-password/+server.ts +++ b/oldApis/reset-password/+server.ts @@ -1,34 +1,34 @@ -import { PUBLIC_SITE_URL } from '$env/static/public' -import { createPasswordResetToken } from '$lib/server/auth-utils.js' -import { error } from '@sveltejs/kit' -import { eq } from 'drizzle-orm' -import { usersTable } from '../../src/lib/server/api/databases/tables' -import { db } from '../../src/lib/server/api/packages/drizzle' +import { PUBLIC_SITE_URL } from '$env/static/public'; +import { createPasswordResetToken } from '$lib/server/auth-utils.js'; +import { error } from '@sveltejs/kit'; +import { eq } from 'drizzle-orm'; +import { usersTable } from '../../src/lib/server/api/databases/postgres/tables'; +import { db } from '../../src/lib/server/api/packages/drizzle'; export async function POST({ locals, request }) { - const { email }: { email: string } = await request.json() + const { email }: { email: string } = await request.json(); if (!locals.user) { - error(401, { message: 'Unauthorized' }) + error(401, { message: 'Unauthorized' }); } const user = await db.query.usersTable.findFirst({ where: eq(usersTable.email, email), - }) + }); if (!user) { error(200, { message: 'Email sent! Please check your email for a link to reset your password.', - }) + }); } - const verificationToken = await createPasswordResetToken(user.id) - const verificationLink = PUBLIC_SITE_URL + verificationToken + const verificationToken = await createPasswordResetToken(user.id); + const verificationLink = PUBLIC_SITE_URL + verificationToken; // TODO: send email - console.log('Verification link: ' + verificationLink) + console.log('Verification link: ' + verificationLink); return new Response(null, { status: 200, - }) + }); } diff --git a/oldApis/reset-password/[token]/+server.ts b/oldApis/reset-password/[token]/+server.ts index 8d6360b..15e91c1 100644 --- a/oldApis/reset-password/[token]/+server.ts +++ b/oldApis/reset-password/[token]/+server.ts @@ -1,34 +1,34 @@ -import { eq } from 'drizzle-orm' -import { isWithinExpirationDate } from 'oslo' -import { password_reset_tokens } from '../../../src/lib/server/api/databases/tables' +import { eq } from 'drizzle-orm'; +import { isWithinExpirationDate } from 'oslo'; +import { password_reset_tokens } from '../../../src/lib/server/api/databases/postgres/tables'; // import { lucia } from '$lib/server/lucia'; -import { db } from '../../../src/lib/server/api/packages/drizzle' +import { db } from '../../../src/lib/server/api/packages/drizzle'; export async function POST({ request, params }) { - const { password } = await request.json() + const { password } = await request.json(); if (typeof password !== 'string' || password.length < 8) { return new Response(null, { status: 400, - }) + }); } - const verificationToken = params.token + const verificationToken = params.token; const token = await db.query.password_reset_tokens.findFirst({ where: eq(password_reset_tokens.id, verificationToken), - }) + }); if (!token) { - await db.delete(password_reset_tokens).where(eq(password_reset_tokens.id, verificationToken)) + await db.delete(password_reset_tokens).where(eq(password_reset_tokens.id, verificationToken)); return new Response(null, { status: 400, - }) + }); } if (!token?.expires_at || !isWithinExpirationDate(token.expires_at)) { return new Response(null, { status: 400, - }) + }); } // await lucia.invalidateUserSessions(token.user_id); @@ -44,5 +44,5 @@ export async function POST({ request, params }) { Location: '/', 'Set-Cookie': sessionCookie.serialize(), }, - }) + }); } diff --git a/package.json b/package.json index 780b649..888bb1a 100644 --- a/package.json +++ b/package.json @@ -5,8 +5,8 @@ "scripts": { "db:push": "drizzle-kit push", "db:generate": "drizzle-kit generate", - "db:migrate": "tsx src/lib/server/api/databases/migrate.ts", - "db:seed": "tsx src/lib/server/api/databases/seed.ts", + "db:migrate": "tsx src/lib/server/api/databases/postgres/migrate.ts", + "db:seed": "tsx src/lib/server/api/databases/postgres/seed.ts", "db:studio": "drizzle-kit studio --verbose", "dev": "NODE_OPTIONS=\"--inspect\" vite dev --host", "build": "vite build", @@ -27,20 +27,20 @@ "@faker-js/faker": "^8.4.1", "@melt-ui/pp": "^0.3.2", "@melt-ui/svelte": "^0.83.0", - "@playwright/test": "^1.48.0", - "@sveltejs/adapter-auto": "^3.2.5", - "@sveltejs/enhanced-img": "^0.3.9", - "@sveltejs/kit": "^2.7.1", + "@playwright/test": "^1.48.2", + "@sveltejs/adapter-auto": "^3.3.1", + "@sveltejs/enhanced-img": "^0.3.10", + "@sveltejs/kit": "^2.7.5", "@sveltejs/vite-plugin-svelte": "4.0.0-next.7", "@types/cookie": "^0.6.0", - "@types/node": "^20.16.11", + "@types/node": "^20.17.6", "@types/pg": "^8.11.10", "@types/qrcode": "^1.5.5", "@typescript-eslint/eslint-plugin": "^7.18.0", "@typescript-eslint/parser": "^7.18.0", "arctic": "^1.9.2", "autoprefixer": "^10.4.20", - "drizzle-kit": "^0.23.2", + "drizzle-kit": "^0.27.1", "eslint": "^8.57.1", "eslint-config-prettier": "^9.1.0", "eslint-plugin-svelte": "2.36.0-next.13", @@ -48,7 +48,7 @@ "just-debounce-it": "^3.2.0", "lucia": "3.2.0", "lucide-svelte": "^0.408.0", - "nodemailer": "^6.9.15", + "nodemailer": "^6.9.16", "postcss": "^8.4.47", "postcss-import": "^16.1.0", "postcss-load-config": "^5.1.0", @@ -57,18 +57,18 @@ "prettier-plugin-svelte": "^3.2.7", "svelte": "5.0.0-next.175", "svelte-check": "^3.8.6", - "svelte-headless-table": "^0.18.2", + "svelte-headless-table": "^0.18.3", "svelte-meta-tags": "^3.1.4", "svelte-preprocess": "^6.0.3", "svelte-sequential-preprocessor": "^2.0.2", "sveltekit-flash-message": "^2.4.4", - "sveltekit-superforms": "^2.19.1", - "tailwindcss": "^3.4.13", + "sveltekit-superforms": "^2.20.0", + "tailwindcss": "^3.4.14", "ts-node": "^10.9.2", - "tslib": "^2.7.0", - "tsx": "^4.19.1", + "tslib": "^2.8.1", + "tsx": "^4.19.2", "typescript": "^5.6.3", - "vite": "^5.4.9", + "vite": "^5.4.10", "vitest": "^1.6.0", "zod": "^3.23.8" }, @@ -92,27 +92,27 @@ "@oslojs/otp": "^1.0.0", "@oslojs/webauthn": "^1.0.0", "@paralleldrive/cuid2": "^2.2.2", - "@scalar/hono-api-reference": "^0.5.154", - "@sveltejs/adapter-node": "^5.2.7", - "@sveltejs/adapter-vercel": "^5.4.5", + "@scalar/hono-api-reference": "^0.5.158", + "@sveltejs/adapter-node": "^5.2.9", + "@sveltejs/adapter-vercel": "^5.4.6", "@types/feather-icons": "^4.29.4", "bits-ui": "^0.21.16", "boardgamegeekclient": "^1.9.1", - "bullmq": "^5.20.0", + "bullmq": "^5.24.0", "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", - "cookie": "^0.6.0", + "cookie": "^1.0.1", "dotenv": "^16.4.5", "dotenv-expand": "^11.0.6", - "drizzle-orm": "^0.32.2", + "drizzle-orm": "^0.36.0", "drizzle-zod": "^0.5.1", "feather-icons": "^4.29.2", "formsnap": "^1.0.1", "handlebars": "^4.7.8", - "hono": "^4.6.4", + "hono": "^4.6.9", "hono-pino": "^0.3.0", "hono-rate-limiter": "^0.4.0", - "hono-zod-openapi": "^0.3.0", + "hono-zod-openapi": "^0.3.1", "html-entities": "^2.5.2", "iconify-icon": "^2.1.0", "ioredis": "^5.4.1", @@ -122,21 +122,21 @@ "mode-watcher": "^0.4.1", "open-props": "^1.7.7", "oslo": "^1.2.1", - "pg": "^8.13.0", - "pino": "^9.4.0", - "pino-pretty": "^11.2.2", - "postgres": "^3.4.4", + "pg": "^8.13.1", + "pino": "^9.5.0", + "pino-pretty": "^11.3.0", + "postgres": "^3.4.5", "qrcode": "^1.5.4", "radix-svelte": "^0.9.0", "rate-limit-redis": "^4.2.0", "reflect-metadata": "^0.2.2", - "stoker": "^1.2.3", + "stoker": "^1.3.0", "svelte-lazy-loader": "^1.0.0", "svelte-sonner": "^0.3.28", "tailwind-merge": "^2.5.4", "tailwind-variants": "^0.2.1", "tailwindcss-animate": "^1.0.7", "tsyringe": "^4.8.0", - "zod-to-json-schema": "^3.23.3" + "zod-to-json-schema": "^3.23.5" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1be6eda..98654ae 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -13,13 +13,13 @@ importers: version: 5.1.0 '@hono/swagger-ui': specifier: ^0.4.1 - version: 0.4.1(hono@4.6.4) + version: 0.4.1(hono@4.6.9) '@hono/zod-openapi': specifier: ^0.15.3 - version: 0.15.3(hono@4.6.4)(zod@3.23.8) + version: 0.15.3(hono@4.6.9)(zod@3.23.8) '@hono/zod-validator': specifier: ^0.2.2 - version: 0.2.2(hono@4.6.4)(zod@3.23.8) + version: 0.2.2(hono@4.6.9)(zod@3.23.8) '@iconify-icons/line-md': specifier: ^1.2.30 version: 1.2.30 @@ -31,7 +31,7 @@ importers: version: 3.5.6 '@lucia-auth/adapter-drizzle': specifier: ^1.1.0 - version: 1.1.0(drizzle-orm@0.32.2(@neondatabase/serverless@0.9.5)(@types/pg@8.11.10)(pg@8.13.0)(postgres@3.4.4))(lucia@3.2.0) + version: 1.1.0(drizzle-orm@0.36.0(@neondatabase/serverless@0.9.5)(@types/pg@8.11.10)(pg@8.13.1)(postgres@3.4.5))(lucia@3.2.0) '@lukeed/uuid': specifier: ^2.0.1 version: 2.0.1 @@ -63,14 +63,14 @@ importers: specifier: ^2.2.2 version: 2.2.2 '@scalar/hono-api-reference': - specifier: ^0.5.154 - version: 0.5.154(hono@4.6.4) + specifier: ^0.5.158 + version: 0.5.158(hono@4.6.9) '@sveltejs/adapter-node': - specifier: ^5.2.7 - version: 5.2.7(@sveltejs/kit@2.7.1(@sveltejs/vite-plugin-svelte@4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.9(@types/node@20.16.11)))(svelte@5.0.0-next.175)(vite@5.4.9(@types/node@20.16.11))) + specifier: ^5.2.9 + version: 5.2.9(@sveltejs/kit@2.7.5(@sveltejs/vite-plugin-svelte@4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.10(@types/node@20.17.6)))(svelte@5.0.0-next.175)(vite@5.4.10(@types/node@20.17.6))) '@sveltejs/adapter-vercel': - specifier: ^5.4.5 - version: 5.4.5(@sveltejs/kit@2.7.1(@sveltejs/vite-plugin-svelte@4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.9(@types/node@20.16.11)))(svelte@5.0.0-next.175)(vite@5.4.9(@types/node@20.16.11))) + specifier: ^5.4.6 + version: 5.4.6(@sveltejs/kit@2.7.5(@sveltejs/vite-plugin-svelte@4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.10(@types/node@20.17.6)))(svelte@5.0.0-next.175)(vite@5.4.10(@types/node@20.17.6))) '@types/feather-icons': specifier: ^4.29.4 version: 4.29.4 @@ -81,8 +81,8 @@ importers: specifier: ^1.9.1 version: 1.9.1 bullmq: - specifier: ^5.20.0 - version: 5.20.0 + specifier: ^5.24.0 + version: 5.24.0 class-variance-authority: specifier: ^0.7.0 version: 0.7.0 @@ -90,8 +90,8 @@ importers: specifier: ^2.1.1 version: 2.1.1 cookie: - specifier: ^0.6.0 - version: 0.6.0 + specifier: ^1.0.1 + version: 1.0.1 dotenv: specifier: ^16.4.5 version: 16.4.5 @@ -99,32 +99,32 @@ importers: specifier: ^11.0.6 version: 11.0.6 drizzle-orm: - specifier: ^0.32.2 - version: 0.32.2(@neondatabase/serverless@0.9.5)(@types/pg@8.11.10)(pg@8.13.0)(postgres@3.4.4) + specifier: ^0.36.0 + version: 0.36.0(@neondatabase/serverless@0.9.5)(@types/pg@8.11.10)(pg@8.13.1)(postgres@3.4.5) drizzle-zod: specifier: ^0.5.1 - version: 0.5.1(drizzle-orm@0.32.2(@neondatabase/serverless@0.9.5)(@types/pg@8.11.10)(pg@8.13.0)(postgres@3.4.4))(zod@3.23.8) + version: 0.5.1(drizzle-orm@0.36.0(@neondatabase/serverless@0.9.5)(@types/pg@8.11.10)(pg@8.13.1)(postgres@3.4.5))(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.19.1(@sveltejs/kit@2.7.1(@sveltejs/vite-plugin-svelte@4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.9(@types/node@20.16.11)))(svelte@5.0.0-next.175)(vite@5.4.9(@types/node@20.16.11)))(@types/json-schema@7.0.15)(svelte@5.0.0-next.175)) + version: 1.0.1(svelte@5.0.0-next.175)(sveltekit-superforms@2.20.0(@sveltejs/kit@2.7.5(@sveltejs/vite-plugin-svelte@4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.10(@types/node@20.17.6)))(svelte@5.0.0-next.175)(vite@5.4.10(@types/node@20.17.6)))(@types/json-schema@7.0.15)(svelte@5.0.0-next.175)(typescript@5.6.3)) handlebars: specifier: ^4.7.8 version: 4.7.8 hono: - specifier: ^4.6.4 - version: 4.6.4 + specifier: ^4.6.9 + version: 4.6.9 hono-pino: specifier: ^0.3.0 - version: 0.3.0(hono@4.6.4)(pino@9.4.0) + version: 0.3.0(hono@4.6.9)(pino@9.5.0) hono-rate-limiter: specifier: ^0.4.0 - version: 0.4.0(hono@4.6.4) + version: 0.4.0(hono@4.6.9) hono-zod-openapi: - specifier: ^0.3.0 - version: 0.3.0(hono@4.6.4)(zod@3.23.8) + specifier: ^0.3.1 + version: 0.3.1(hono@4.6.9)(zod@3.23.8) html-entities: specifier: ^2.5.2 version: 2.5.2 @@ -153,17 +153,17 @@ importers: specifier: ^1.2.1 version: 1.2.1 pg: - specifier: ^8.13.0 - version: 8.13.0 + specifier: ^8.13.1 + version: 8.13.1 pino: - specifier: ^9.4.0 - version: 9.4.0 + specifier: ^9.5.0 + version: 9.5.0 pino-pretty: - specifier: ^11.2.2 - version: 11.2.2 + specifier: ^11.3.0 + version: 11.3.0 postgres: - specifier: ^3.4.4 - version: 3.4.4 + specifier: ^3.4.5 + version: 3.4.5 qrcode: specifier: ^1.5.4 version: 1.5.4 @@ -177,8 +177,8 @@ importers: specifier: ^0.2.2 version: 0.2.2 stoker: - specifier: ^1.2.3 - version: 1.2.3(@asteasolutions/zod-to-openapi@7.1.2(zod@3.23.8))(@hono/zod-openapi@0.15.3(hono@4.6.4)(zod@3.23.8))(hono@4.6.4)(openapi3-ts@4.4.0) + specifier: ^1.3.0 + version: 1.3.0(@asteasolutions/zod-to-openapi@7.1.2(zod@3.23.8))(@hono/zod-openapi@0.15.3(hono@4.6.9)(zod@3.23.8))(hono@4.6.9)(openapi3-ts@4.4.0) svelte-lazy-loader: specifier: ^1.0.0 version: 1.0.0 @@ -190,16 +190,16 @@ importers: version: 2.5.4 tailwind-variants: specifier: ^0.2.1 - version: 0.2.1(tailwindcss@3.4.13(ts-node@10.9.2(@types/node@20.16.11)(typescript@5.6.3))) + version: 0.2.1(tailwindcss@3.4.14(ts-node@10.9.2(@types/node@20.17.6)(typescript@5.6.3))) tailwindcss-animate: specifier: ^1.0.7 - version: 1.0.7(tailwindcss@3.4.13(ts-node@10.9.2(@types/node@20.16.11)(typescript@5.6.3))) + version: 1.0.7(tailwindcss@3.4.14(ts-node@10.9.2(@types/node@20.17.6)(typescript@5.6.3))) tsyringe: specifier: ^4.8.0 version: 4.8.0 zod-to-json-schema: - specifier: ^3.23.3 - version: 3.23.3(zod@3.23.8) + specifier: ^3.23.5 + version: 3.23.5(zod@3.23.8) devDependencies: '@biomejs/biome': specifier: 1.8.3 @@ -214,26 +214,26 @@ importers: specifier: ^0.83.0 version: 0.83.0(svelte@5.0.0-next.175) '@playwright/test': - specifier: ^1.48.0 - version: 1.48.0 + specifier: ^1.48.2 + version: 1.48.2 '@sveltejs/adapter-auto': - specifier: ^3.2.5 - version: 3.2.5(@sveltejs/kit@2.7.1(@sveltejs/vite-plugin-svelte@4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.9(@types/node@20.16.11)))(svelte@5.0.0-next.175)(vite@5.4.9(@types/node@20.16.11))) + specifier: ^3.3.1 + version: 3.3.1(@sveltejs/kit@2.7.5(@sveltejs/vite-plugin-svelte@4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.10(@types/node@20.17.6)))(svelte@5.0.0-next.175)(vite@5.4.10(@types/node@20.17.6))) '@sveltejs/enhanced-img': - specifier: ^0.3.9 - version: 0.3.9(rollup@4.24.0)(svelte@5.0.0-next.175)(vite@5.4.9(@types/node@20.16.11)) + specifier: ^0.3.10 + version: 0.3.10(rollup@4.24.0)(svelte@5.0.0-next.175)(vite@5.4.10(@types/node@20.17.6)) '@sveltejs/kit': - specifier: ^2.7.1 - version: 2.7.1(@sveltejs/vite-plugin-svelte@4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.9(@types/node@20.16.11)))(svelte@5.0.0-next.175)(vite@5.4.9(@types/node@20.16.11)) + specifier: ^2.7.5 + version: 2.7.5(@sveltejs/vite-plugin-svelte@4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.10(@types/node@20.17.6)))(svelte@5.0.0-next.175)(vite@5.4.10(@types/node@20.17.6)) '@sveltejs/vite-plugin-svelte': specifier: 4.0.0-next.7 - version: 4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.9(@types/node@20.16.11)) + version: 4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.10(@types/node@20.17.6)) '@types/cookie': specifier: ^0.6.0 version: 0.6.0 '@types/node': - specifier: ^20.16.11 - version: 20.16.11 + specifier: ^20.17.6 + version: 20.17.6 '@types/pg': specifier: ^8.11.10 version: 8.11.10 @@ -253,8 +253,8 @@ importers: specifier: ^10.4.20 version: 10.4.20(postcss@8.4.47) drizzle-kit: - specifier: ^0.23.2 - version: 0.23.2 + specifier: ^0.27.1 + version: 0.27.1 eslint: specifier: ^8.57.1 version: 8.57.1 @@ -263,7 +263,7 @@ importers: version: 9.1.0(eslint@8.57.1) eslint-plugin-svelte: specifier: 2.36.0-next.13 - version: 2.36.0-next.13(eslint@8.57.1)(svelte@5.0.0-next.175)(ts-node@10.9.2(@types/node@20.16.11)(typescript@5.6.3)) + version: 2.36.0-next.13(eslint@8.57.1)(svelte@5.0.0-next.175)(ts-node@10.9.2(@types/node@20.17.6)(typescript@5.6.3)) just-clone: specifier: ^6.2.0 version: 6.2.0 @@ -277,8 +277,8 @@ importers: specifier: ^0.408.0 version: 0.408.0(svelte@5.0.0-next.175) nodemailer: - specifier: ^6.9.15 - version: 6.9.15 + specifier: ^6.9.16 + version: 6.9.16 postcss: specifier: ^8.4.47 version: 8.4.47 @@ -287,7 +287,7 @@ importers: version: 16.1.0(postcss@8.4.47) postcss-load-config: specifier: ^5.1.0 - version: 5.1.0(jiti@1.21.6)(postcss@8.4.47)(tsx@4.19.1) + version: 5.1.0(jiti@1.21.6)(postcss@8.4.47)(tsx@4.19.2) postcss-preset-env: specifier: ^9.6.0 version: 9.6.0(postcss@8.4.47) @@ -302,46 +302,46 @@ importers: version: 5.0.0-next.175 svelte-check: specifier: ^3.8.6 - version: 3.8.6(postcss-load-config@5.1.0(jiti@1.21.6)(postcss@8.4.47)(tsx@4.19.1))(postcss@8.4.47)(svelte@5.0.0-next.175) + version: 3.8.6(postcss-load-config@5.1.0(jiti@1.21.6)(postcss@8.4.47)(tsx@4.19.2))(postcss@8.4.47)(svelte@5.0.0-next.175) svelte-headless-table: - specifier: ^0.18.2 - version: 0.18.2(svelte@5.0.0-next.175) + specifier: ^0.18.3 + version: 0.18.3(svelte@5.0.0-next.175) svelte-meta-tags: specifier: ^3.1.4 version: 3.1.4(svelte@5.0.0-next.175)(typescript@5.6.3) svelte-preprocess: specifier: ^6.0.3 - version: 6.0.3(postcss-load-config@5.1.0(jiti@1.21.6)(postcss@8.4.47)(tsx@4.19.1))(postcss@8.4.47)(svelte@5.0.0-next.175)(typescript@5.6.3) + version: 6.0.3(postcss-load-config@5.1.0(jiti@1.21.6)(postcss@8.4.47)(tsx@4.19.2))(postcss@8.4.47)(svelte@5.0.0-next.175)(typescript@5.6.3) svelte-sequential-preprocessor: specifier: ^2.0.2 version: 2.0.2 sveltekit-flash-message: specifier: ^2.4.4 - version: 2.4.4(@sveltejs/kit@2.7.1(@sveltejs/vite-plugin-svelte@4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.9(@types/node@20.16.11)))(svelte@5.0.0-next.175)(vite@5.4.9(@types/node@20.16.11)))(svelte@5.0.0-next.175) + version: 2.4.4(@sveltejs/kit@2.7.5(@sveltejs/vite-plugin-svelte@4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.10(@types/node@20.17.6)))(svelte@5.0.0-next.175)(vite@5.4.10(@types/node@20.17.6)))(svelte@5.0.0-next.175) sveltekit-superforms: - specifier: ^2.19.1 - version: 2.19.1(@sveltejs/kit@2.7.1(@sveltejs/vite-plugin-svelte@4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.9(@types/node@20.16.11)))(svelte@5.0.0-next.175)(vite@5.4.9(@types/node@20.16.11)))(@types/json-schema@7.0.15)(svelte@5.0.0-next.175) + specifier: ^2.20.0 + version: 2.20.0(@sveltejs/kit@2.7.5(@sveltejs/vite-plugin-svelte@4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.10(@types/node@20.17.6)))(svelte@5.0.0-next.175)(vite@5.4.10(@types/node@20.17.6)))(@types/json-schema@7.0.15)(svelte@5.0.0-next.175)(typescript@5.6.3) tailwindcss: - specifier: ^3.4.13 - version: 3.4.13(ts-node@10.9.2(@types/node@20.16.11)(typescript@5.6.3)) + specifier: ^3.4.14 + version: 3.4.14(ts-node@10.9.2(@types/node@20.17.6)(typescript@5.6.3)) ts-node: specifier: ^10.9.2 - version: 10.9.2(@types/node@20.16.11)(typescript@5.6.3) + version: 10.9.2(@types/node@20.17.6)(typescript@5.6.3) tslib: - specifier: ^2.7.0 - version: 2.7.0 + specifier: ^2.8.1 + version: 2.8.1 tsx: - specifier: ^4.19.1 - version: 4.19.1 + specifier: ^4.19.2 + version: 4.19.2 typescript: specifier: ^5.6.3 version: 5.6.3 vite: - specifier: ^5.4.9 - version: 5.4.9(@types/node@20.16.11) + specifier: ^5.4.10 + version: 5.4.10(@types/node@20.17.6) vitest: specifier: ^1.6.0 - version: 1.6.0(@types/node@20.16.11) + version: 1.6.0(@types/node@20.17.6) zod: specifier: ^3.23.8 version: 3.23.8 @@ -668,8 +668,13 @@ packages: peerDependencies: postcss: ^8.4 - '@drizzle-team/brocli@0.8.2': - resolution: {integrity: sha512-zTrFENsqGvOkBOuHDC1pXCkDXNd2UhP4lI3gYGhQ1R1SPeAAfqzPsV1dcpMy4uNU6kB5VpU5NGhvwxVNETR02A==} + '@drizzle-team/brocli@0.10.2': + resolution: {integrity: sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w==} + + '@effect/schema@0.75.5': + resolution: {integrity: sha512-TQInulTVCuF+9EIbJpyLP6dvxbQJMphrnRqgexm/Ze39rSjfhJuufF7XvU3SxTgg3HnL7B/kpORTJbHhlE6thw==} + peerDependencies: + effect: ^3.9.2 '@emnapi/core@0.45.0': resolution: {integrity: sha512-DPWjcUDQkCeEM4VnljEOEcXdAD7pp8zSZsgOujk/LGIwCXWbXJngin+MO4zbH429lzeC3WbYLGjE2MaUOwzpyw==} @@ -688,9 +693,11 @@ packages: '@esbuild-kit/core-utils@3.3.2': resolution: {integrity: sha512-sPRAnw9CdSsRmEtnsl2WXWdyquogVpB3yZ3dgwJfe8zrOzTsV7cJvmwrKVa+0ma5BoiGJ+BoqkMvawbayKUsqQ==} + deprecated: 'Merged into tsx: https://tsx.is' '@esbuild-kit/esm-loader@2.6.5': resolution: {integrity: sha512-FxEMIkJKnodyA1OaCUoEvbYRkoZlLZ4d/eXFu9Fh8CbBBgP5EmZxrfTRyN0qpXZ4vOvqnE5YdRdcrmUUXuU+dA==} + deprecated: 'Merged into tsx: https://tsx.is' '@esbuild/aix-ppc64@0.19.12': resolution: {integrity: sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==} @@ -2011,8 +2018,8 @@ packages: resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} - '@playwright/test@1.48.0': - resolution: {integrity: sha512-W5lhqPUVPqhtc/ySvZI5Q8X2ztBOUgZ8LbAFy0JQgrXZs2xaILrUcNO3rQjwbLPfGK13+rZsDa1FpG+tqYkT5w==} + '@playwright/test@1.48.2': + resolution: {integrity: sha512-54w1xCWfXuax7dz4W2M9uw0gDyh+ti/0K/MxcCUxChFh37kkdxPdfZDw5QBbuPUJHr1CiHJ1hXgSs+GgeQc5Zw==} engines: {node: '>=18'} hasBin: true @@ -2023,8 +2030,8 @@ packages: resolution: {integrity: sha512-B4iV6QxW//Fn17+qF1EMZRmoThIUJlCtcO85yoRDJnMyHeAthjz4ig9OTkfGGXKtQhcdPX0me75gU5K9J897+w==} engines: {node: '>=18.16.0'} - '@rollup/plugin-commonjs@28.0.0': - resolution: {integrity: sha512-BJcu+a+Mpq476DMXG+hevgPSl56bkUoi88dKT8t3RyUp8kGuOh+2bU8Gs7zXDlu+fyZggnJ+iOBGrb/O1SorYg==} + '@rollup/plugin-commonjs@28.0.1': + resolution: {integrity: sha512-+tNWdlWKbpB3WgBN7ijjYkq9X5uhjmcvyjEght4NmH5fAU++zfQzAJ6wumLS+dNcvwEZhKx2Z+skY8m7v0wGSA==} engines: {node: '>=16.0.0 || 14 >= 14.17'} peerDependencies: rollup: ^2.68.0||^3.0.0||^4.0.0 @@ -2143,18 +2150,18 @@ packages: cpu: [x64] os: [win32] - '@scalar/hono-api-reference@0.5.154': - resolution: {integrity: sha512-v5ufLt7OG27BsPqO20YtnLvIcTsRZjU5jbFbpMUguW+SFgRWh1gji2RlC4BfK5x9hrK1Ek0hz46OMYweWp4I3w==} + '@scalar/hono-api-reference@0.5.158': + resolution: {integrity: sha512-2P7l/ivuC/RWpAKddLtkqIZ89TA5/QlpfEBrdpH9/yjn4NpR5XkbtT+/8uZVELCyjfUMpGcaNXrxuJERFJ3sxA==} engines: {node: '>=18'} peerDependencies: hono: ^4.0.0 - '@scalar/openapi-types@0.1.3': - resolution: {integrity: sha512-UPUquC/ZSAdeSDbpdqMzeBVcgeytqJTj9P3QR4X9IRti5I1jXoCOUBY84CGGLXfcWquB7wfwHxn3DqQEnQmBVw==} + '@scalar/openapi-types@0.1.4': + resolution: {integrity: sha512-+wRXgmqzgDnj8Dxqf4OOPMPo4or/LRd1Bsy4pnrIW0yBt8rKSdtBb+jH/aRnhgDDmKVjWxJ+KFk7WlSKvZwNTw==} engines: {node: '>=18'} - '@scalar/types@0.0.15': - resolution: {integrity: sha512-9fSBoAuJTsmnt2FTkm/F6LRq+TIeeRI0H8wz+7J3pHfcw0YGFEZrRZOo1WZ8fpovD0fQgg+r4BPrtN8+qJ12wQ==} + '@scalar/types@0.0.18': + resolution: {integrity: sha512-gfJB/e9Rq/vjsiWlNwBkaIAZVb9v5guHQB5uVoVFcU0gdAuXni0KVxFxl3gGTu2zhBdB+DkixjyPcNzpqwksmA==} engines: {node: '>=18'} '@sideway/address@4.1.5': @@ -2172,29 +2179,29 @@ packages: '@sinclair/typebox@0.32.35': resolution: {integrity: sha512-Ul3YyOTU++to8cgNkttakC0dWvpERr6RYoHO2W47DLbFvrwBDJUY31B1sImH6JZSYc4Kt4PyHtoPNu+vL2r2dA==} - '@sveltejs/adapter-auto@3.2.5': - resolution: {integrity: sha512-27LR+uKccZ62lgq4N/hvyU2G+hTP9fxWEAfnZcl70HnyfAjMSsGk1z/SjAPXNCD1mVJIE7IFu3TQ8cQ/UH3c0A==} + '@sveltejs/adapter-auto@3.3.1': + resolution: {integrity: sha512-5Sc7WAxYdL6q9j/+D0jJKjGREGlfIevDyHSQ2eNETHcB1TKlQWHcAo8AS8H1QdjNvSXpvOwNjykDUHPEAyGgdQ==} peerDependencies: '@sveltejs/kit': ^2.0.0 - '@sveltejs/adapter-node@5.2.7': - resolution: {integrity: sha512-lamfhxiRIaPtuhNpJE9h8haRo/I5TzuimaiphWUGXsRCDqYWsLKtF+mo61awkBIxm+XggC4n1iaNqwf+Kt7K4Q==} + '@sveltejs/adapter-node@5.2.9': + resolution: {integrity: sha512-51euNrx0AcaTu8//wDfVh7xmqQSVgU52rfinE/MwvGkJa4nHPJMHmzv6+OIpmxg7gZaF6+5NVlxnieCzxLD59g==} peerDependencies: '@sveltejs/kit': ^2.4.0 - '@sveltejs/adapter-vercel@5.4.5': - resolution: {integrity: sha512-SROpUbjSZ1Xni4xuS22dunXFLjYzvTZwppqixIQFzTrf9oWcZEm2OfO+VgnrOT67LOcWQefJp7VSrpmjV691yQ==} + '@sveltejs/adapter-vercel@5.4.6': + resolution: {integrity: sha512-jUFc5NEPgBzcqxDgwAMk/lIxFuYA8MP8KcPh/UWFZH3fDZ/igi1h/do/nKsEmPxF5NuJVvI2mxX6TaJZzHcpTg==} peerDependencies: '@sveltejs/kit': ^2.4.0 - '@sveltejs/enhanced-img@0.3.9': - resolution: {integrity: sha512-hDhoIbkDAY08II/1DWeY2lGMY9nhETC96B2HTbxoI6EDqxLErBDKqnRN3QQRuMJATxPGVNhCKUkuARi4TCLtpQ==} + '@sveltejs/enhanced-img@0.3.10': + resolution: {integrity: sha512-1JxjthN6yb1md3rSFbHRDBi/Jj2R9EjE06vh9zbWgYxm5d4UUphTzNICJTis8bkIDQilbAokrkaQtfRpKSE7qg==} peerDependencies: svelte: ^4.0.0 || ^5.0.0-next.0 vite: '>= 5.0.0' - '@sveltejs/kit@2.7.1': - resolution: {integrity: sha512-TBVnkwgYQT3EafGQK6Eyh5FlLEBlRhCmqPTwcdOs+QdnyUc3eCAxRWtXlFxIWtmk6pqv11zdng8qTpThdTogew==} + '@sveltejs/kit@2.7.5': + resolution: {integrity: sha512-8WIrVch2Ze2Rq3eIMPTqIIRFPM2zGQcGKHim2z43KVRdgdtYWBugAQ7nemH9ATnzlvbgztk6hwhEZOi8A8ZOPg==} engines: {node: '>=18.13'} hasBin: true peerDependencies: @@ -2250,8 +2257,8 @@ packages: '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} - '@types/node@20.16.11': - resolution: {integrity: sha512-y+cTCACu92FyA5fgQSAI8A1H429g7aSK2HsO7K4XYUWc4dY5IUz55JSDIYT6/VsOLfGy8vmvQYC2hfb0iF16Uw==} + '@types/node@20.17.6': + resolution: {integrity: sha512-VEI7OdvK2wP7XHnsuXbAJnEpEkF6NjSN45QJlL4VGqZSXsnicpesdTWsg9RISeSdYd3yeRj/y3k5KGjUXYnFwQ==} '@types/pg@8.11.10': resolution: {integrity: sha512-LczQUW4dbOQzsH2RQ5qoeJ6qJPdrcM/DcMLoqWQkMLMsq83J5lAX3LXjdkWdpscFy67JSOWDnh7Ny/sPFykmkg==} @@ -2271,8 +2278,8 @@ packages: '@types/validator@13.12.2': resolution: {integrity: sha512-6SlHBzUW8Jhf3liqrGGXyTJSIFe4nqlJ5A5KaMZ2l/vbM3Wh3KSybots/wfWVzNLK4D1NZluDlSQIbIEPx6oyA==} - '@typeschema/class-validator@0.2.0': - resolution: {integrity: sha512-zq0qeflVu1Z6D0ttkqAWZMtxJeNEQ70yo/025sV0jujiOOgQx38JXrky77nSWWPp2E1KIMtgkToQbkzkXyW5yg==} + '@typeschema/class-validator@0.3.0': + resolution: {integrity: sha512-OJSFeZDIQ8EK1HTljKLT5CItM2wsbgczLN8tMEfz3I1Lmhc5TBfkZ0eikFzUC16tI3d1Nag7um6TfCgp2I2Bww==} peerDependencies: class-validator: ^0.14.1 peerDependenciesMeta: @@ -2348,8 +2355,8 @@ packages: '@ungap/structured-clone@1.2.0': resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} - '@unhead/schema@1.11.9': - resolution: {integrity: sha512-0V37bxG4sQuiLw3M5DMD+b99ndOOngecMlekQ122TDvBb24W8rWwkHhXvAu5eFg6bQXPdQF1A0U0PuRMcCj/ZA==} + '@unhead/schema@1.11.11': + resolution: {integrity: sha512-xSGsWHPBYcMV/ckQeImbrVu6ddeRnrdDCgXUKv3xIjGBY+ob/96V80lGX8FKWh8GwdFSwhblISObKlDAt5K9ZQ==} '@vercel/nft@0.27.4': resolution: {integrity: sha512-Rioz3LJkEKicKCi9BSyc1RXZ5R6GmXosFMeBSThh6msWSOiArKhb7c75MiWwZEgPL7x0/l3TAfH/l0cxKNuUFA==} @@ -2554,8 +2561,8 @@ packages: buffer@6.0.3: resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} - bullmq@5.20.0: - resolution: {integrity: sha512-eCJyYJqNUl9swC39x2fVm1BUv5BuO/nv2eAcAsz58znue0ZCYgSG+yWXZeauRG98Jl0UIBcPgJtbF+c9Wd+Odg==} + bullmq@5.24.0: + resolution: {integrity: sha512-rNWOg4opfHOhZjWWr1aIjfw2nUFB91F9qwIT49CdRypL4FznmHAqamTnw2EcZlj2KeFswV50tisZwq/h1yMUAw==} bytes@3.1.2: resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} @@ -2686,6 +2693,10 @@ packages: resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==} engines: {node: '>= 0.6'} + cookie@1.0.1: + resolution: {integrity: sha512-Xd8lFX4LM9QEEwxQpF9J9NTUh8pmdJO0cyRJhFiDoLTk2eH8FXlRv2IFGYVadZpqI3j8fhNrSdKCeYPxiAhLXw==} + engines: {node: '>=18'} + core-js@3.38.1: resolution: {integrity: sha512-OP35aUorbU3Zvlx7pjsFdu1rGNnD4pgw/CWoYzRY3t2EzoVT7shKHY1dlAy3f41cGIO7ZDPQimhGFTlEYkG/Hw==} @@ -2838,17 +2849,18 @@ packages: resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==} engines: {node: '>=12'} - drizzle-kit@0.23.2: - resolution: {integrity: sha512-NWkQ7GD2OTbQ7HzcjsaCOf3n0tlFPSEAF38fvDpwDj8jRbGWGFtN2cD8I8wp4lU+5Os/oyP2xycTKGLHdPipUw==} + drizzle-kit@0.27.1: + resolution: {integrity: sha512-4BNA0T2blN+jW5wSwhtc+FIlCMuxYSMWCnYYdOBi5rttwq8aVXRUid0d0NCzcBKtZQSPZGAUxy+TXr7Q1OgEug==} hasBin: true - drizzle-orm@0.32.2: - resolution: {integrity: sha512-3fXKzPzrgZIcnWCSLiERKN5Opf9Iagrag75snfFlKeKSYB1nlgPBshzW3Zn6dQymkyiib+xc4nIz0t8U+Xdpuw==} + drizzle-orm@0.36.0: + resolution: {integrity: sha512-6BETYPdKSR7cDHC0ZfqZk2VrKJ8n/Rfd3ajFPsAbc69gi87nwZ6oBA2wUGMELHA0tQE4kUKN0Ds00LUZQ6Z69A==} peerDependencies: '@aws-sdk/client-rds-data': '>=3' '@cloudflare/workers-types': '>=3' - '@electric-sql/pglite': '>=0.1.1' - '@libsql/client': '*' + '@electric-sql/pglite': '>=0.2.0' + '@libsql/client': '>=0.10.0' + '@libsql/client-wasm': '>=0.10.0' '@neondatabase/serverless': '>=0.1' '@op-engineering/op-sqlite': '>=2' '@opentelemetry/api': ^1.4.1 @@ -2882,6 +2894,8 @@ packages: optional: true '@libsql/client': optional: true + '@libsql/client-wasm': + optional: true '@neondatabase/serverless': optional: true '@op-engineering/op-sqlite': @@ -2943,6 +2957,9 @@ packages: ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} + effect@3.9.2: + resolution: {integrity: sha512-1sx/v1HTWHTodXfzWxAFg+SCF+ACgpJVruaAMIh/NmDVvrUsf0x9PzpXvkgJUbQ1fMdmKYK//FqxeHSQ+Zxv/Q==} + electron-to-chromium@1.5.32: resolution: {integrity: sha512-M+7ph0VGBQqqpTT2YrabjNKSQ2fEl9PVx6AK3N558gDH9NO8O6XN9SXXFWRo9u9PbEg/bWq+tjXQr+eXmxubCw==} @@ -3115,6 +3132,10 @@ packages: resolution: {integrity: sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng==} engines: {node: '>= 0.10.0'} + fast-check@3.22.0: + resolution: {integrity: sha512-8HKz3qXqnHYp/VCNn2qfjHdAdcI8zcSqOyX64GOMukp7SL2bfzfeDKjSd+UyECtejccaZv3LcvZTm9YDD22iCQ==} + engines: {node: '>=8.0.0'} + fast-copy@3.0.2: resolution: {integrity: sha512-dl0O9Vhju8IrcLndv2eU4ldt1ftXMqqfgN4H1cpmGV7P6jeB9FwpN9a2c8DPGE1Ys88rNUJVYDHq73CGAGOPfQ==} @@ -3145,8 +3166,8 @@ packages: fastq@1.17.1: resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} - fdir@6.4.0: - resolution: {integrity: sha512-3oB133prH1o4j/L5lLW7uOCF1PlD+/It2L0eL/iAqWMB91RBbqTewABqxhj0ibBd90EEmWZq7ntIWzVaWcXTGQ==} + fdir@6.4.2: + resolution: {integrity: sha512-KnhMXsKSPZlAhp7+IjUkRZKPb4fUyccpDrdFXbi4QL1qkmFh9kVY09Yox+n4MaOb3lHZ1Tv829C3oaaXoMYPDQ==} peerDependencies: picomatch: ^3 || ^4 peerDependenciesMeta: @@ -3337,14 +3358,14 @@ packages: peerDependencies: hono: ^4.1.1 - hono-zod-openapi@0.3.0: - resolution: {integrity: sha512-Ledae1WTC+ct7Mj61y+KnrwgPm34CkU1wTt85C2bBoD60b7DR7ExJEUj77tZU7O8Y7G8p9NWwYngfbzGDTBtYA==} + hono-zod-openapi@0.3.1: + resolution: {integrity: sha512-7i0cQzbt5vKtXzMYp3URbvEJh/sgoNox9WI1sHyMZQ/9cB1KX20SNngZS9UBvK+QQPhOWRR2gR4ZvdiEIcvxQQ==} peerDependencies: hono: ^4.6.3 zod: ^3.21.4 - hono@4.6.4: - resolution: {integrity: sha512-T5WqBkTOcIQblqBKB5mpzaH/A+dSpvVe938xZJCHOmOuYfF7DSwE/9/10+BMvwSPq9N/f6LiQ38HxrZSQOsXKw==} + hono@4.6.9: + resolution: {integrity: sha512-p/pN5yZLuZaHzyAOT2nw2/Ud6HhJHYmDNGH6Ck1OWBhPMVeM1r74jbCRwNi0gyFRjjbsGgoHbOyj7mT1PDNbTw==} engines: {node: '>=16.9.0'} hookable@5.5.3: @@ -3590,6 +3611,9 @@ packages: magic-string@0.30.11: resolution: {integrity: sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==} + magic-string@0.30.12: + resolution: {integrity: sha512-Ea8I3sQMVXr8JhN4z+H/d8zwo+tYDgHE9+5G4Wnrwhs0gaK9fXTKx0Tw5Xwsd/bCPTTZNRAdpyzvoeORe9LYpw==} + make-dir@3.1.0: resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==} engines: {node: '>=8'} @@ -3718,8 +3742,8 @@ packages: resolution: {integrity: sha512-P0efT1C9jIdVRefqjzOQ9Xml57zpOXnIuS+csaB4MdZbTdmGDLo8XhzBG1N7aO11gKDDkJvBLULeFTo46wwreA==} hasBin: true - msgpackr@1.11.0: - resolution: {integrity: sha512-I8qXuuALqJe5laEBYoFykChhSXLikZmUhccjGsPuSJ/7uPip2TJ7lwdIQwWSAi0jGZDXv4WOP8Qg65QZRuXxXw==} + msgpackr@1.11.2: + resolution: {integrity: sha512-F9UngXRlPyWCDEASDpTf6c9uNhGPTqnTeLVt7bN+bU1eajoR/8V9ys2BRaV5C/e5ihE6sJ9uPIKaYt6bFuO32g==} mz@2.7.0: resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} @@ -3767,8 +3791,8 @@ packages: node-releases@2.0.18: resolution: {integrity: sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==} - nodemailer@6.9.15: - resolution: {integrity: sha512-AHf04ySLC6CIfuRtRiEYtGEXgRfa6INgWGluDhnxTZhHSKvrBu7lc1VVchQ0d8nPc4cFaZoPq8vkyNoZr0TpGQ==} + nodemailer@6.9.16: + resolution: {integrity: sha512-psAuZdTIRN08HKVd/E8ObdV6NO7NTBY3KsC30F7M4H1OnmLCUNaS56FpYxyb26zWLSyYF9Ozch9KYHhHegsiOQ==} engines: {node: '>=6.0.0'} nopt@5.0.0: @@ -3946,8 +3970,8 @@ packages: resolution: {integrity: sha512-cRL3JpS3lKMGsKaWndugWQoLOCoP+Cic8oseVcbr0qhPzYD5DWXK+RZ9LY9wxRf7RQia4SCwQlXk0q6FCPrVng==} engines: {node: '>=10'} - pg@8.13.0: - resolution: {integrity: sha512-34wkUTh3SxTClfoHB3pQ7bIMvw9dpFU1audQQeZG837fmHfHpr14n/AELVDoOYVDW2h5RDWU78tFjkD+erSBsw==} + pg@8.13.1: + resolution: {integrity: sha512-OUir1A0rPNZlX//c7ksiu7crsGZTKSOXJPgtNiHGIlC9H0lO+NC6ZDYksSgBYY/thSWhnSRBv8w1lieNNGATNQ==} engines: {node: '>= 8.0.0'} peerDependencies: pg-native: '>=3.0.1' @@ -3965,22 +3989,26 @@ packages: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} + picomatch@4.0.2: + resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} + engines: {node: '>=12'} + pify@2.3.0: resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} engines: {node: '>=0.10.0'} - pino-abstract-transport@1.2.0: - resolution: {integrity: sha512-Guhh8EZfPCfH+PMXAb6rKOjGQEoy0xlAIn+irODG5kgfYV+BQ0rGYYWTIel3P5mmyXqkYkPmdIkywsn6QKUR1Q==} + pino-abstract-transport@2.0.0: + resolution: {integrity: sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==} - pino-pretty@11.2.2: - resolution: {integrity: sha512-2FnyGir8nAJAqD3srROdrF1J5BIcMT4nwj7hHSc60El6Uxlym00UbCCd8pYIterstVBFlMyF1yFV8XdGIPbj4A==} + pino-pretty@11.3.0: + resolution: {integrity: sha512-oXwn7ICywaZPHmu3epHGU2oJX4nPmKvHvB/bwrJHlGcbEWaVcotkpyVHMKLKmiVryWYByNp0jpgAcXpFJDXJzA==} hasBin: true pino-std-serializers@7.0.0: resolution: {integrity: sha512-e906FRY0+tV27iq4juKzSYPbUj2do2X2JX4EzSca1631EB2QJQUqGbDuERal7LCtOpxl6x3+nvo9NPZcmjkiFA==} - pino@9.4.0: - resolution: {integrity: sha512-nbkQb5+9YPhQRz/BeQmrWpEknAaqjpAqRK8NwJpmrX/JHu7JuZC5G1CeAwJDJfGes4h+YihC6in3Q2nGb+Y09w==} + pino@9.5.0: + resolution: {integrity: sha512-xSEmD4pLnV54t0NOUN16yCl7RIB1c5UUOse5HSyEXtBp+FgFQyPeDutc+Q2ZO7/22vImV7VfEjH/1zV2QuqvYw==} hasBin: true pirates@4.0.6: @@ -3990,13 +4018,13 @@ packages: pkg-types@1.2.0: resolution: {integrity: sha512-+ifYuSSqOQ8CqP4MbZA5hDpb97n3E8SVWdJe+Wms9kj745lmd3b7EZJiqvmLwAlmRfjrI7Hi5z3kdBJ93lFNPA==} - playwright-core@1.48.0: - resolution: {integrity: sha512-RBvzjM9rdpP7UUFrQzRwR8L/xR4HyC1QXMzGYTbf1vjw25/ya9NRAVnXi/0fvFopjebvyPzsmoK58xxeEOaVvA==} + playwright-core@1.48.2: + resolution: {integrity: sha512-sjjw+qrLFlriJo64du+EK0kJgZzoQPsabGF4lBvsid+3CNIZIYLgnMj9V6JY5VhM2Peh20DJWIVpVljLLnlawA==} engines: {node: '>=18'} hasBin: true - playwright@1.48.0: - resolution: {integrity: sha512-qPqFaMEHuY/ug8o0uteYJSRfMGFikhUysk8ZvAtfKmUK3kc/6oNl/y3EczF8OFGYIi/Ex2HspMfzYArk6+XQSA==} + playwright@1.48.2: + resolution: {integrity: sha512-NjYvYgp4BPmiwfe31j4gHLa3J7bD2WiBz8Lk2RoSsmX38SVIARZ18VYjxLjAcDsAhA+F4iSEXTSGgjua0rrlgQ==} engines: {node: '>=18'} hasBin: true @@ -4278,8 +4306,8 @@ packages: postgres-range@1.1.4: resolution: {integrity: sha512-i/hbxIE9803Alj/6ytL7UHQxRvZkI9O4Sy+J3HGc4F4oo/2eQAjTSNJ0bfxyse3bH0nuVesCk+3IRLaMtG3H6w==} - postgres@3.4.4: - resolution: {integrity: sha512-IbyN+9KslkqcXa8AO9fxpk97PA4pzewvpi2B3Dwy9u4zpV32QicaEdgmF3eSQUzdRk7ttDHQejNgAEr4XoeH4A==} + postgres@3.4.5: + resolution: {integrity: sha512-cDWgoah1Gez9rN3H4165peY9qfpEo+SA61oQv65O3cRUE1pOEoJWwddwcqKE8XZYjbblOJlYDlLV4h67HrEVDg==} engines: {node: '>=12'} prelude-ls@1.2.1: @@ -4322,6 +4350,9 @@ packages: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} + pure-rand@6.1.0: + resolution: {integrity: sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==} + qrcode@1.5.4: resolution: {integrity: sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg==} engines: {node: '>=10.13.0'} @@ -4567,8 +4598,8 @@ packages: std-env@3.7.0: resolution: {integrity: sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==} - stoker@1.2.3: - resolution: {integrity: sha512-i1EKytlmCSHKcyO2O2x5p/z8zzUM0w7o7O2PLw8oK6RZDDu7Cy8YuPM2ERPGZ7AdXS9XpnbQq2hw5k3h3cQxrw==} + stoker@1.3.0: + resolution: {integrity: sha512-ywRokjO8jKb65z6qJVJbzilQJzcoly8/bwyodp6sZC0ZQmn/zfDCOkjcw6hhNKWE8eSVGanZUIXEPab5MTxnmA==} peerDependencies: '@asteasolutions/zod-to-openapi': ^7.0.0 '@hono/zod-openapi': ^0.16.0 @@ -4647,8 +4678,8 @@ packages: svelte: optional: true - svelte-headless-table@0.18.2: - resolution: {integrity: sha512-dnDTaXW5CNzRUjHVbc/Hb0Zv80zU4VcIUnAja6OuZriXvim1AqcWYQCHPRzBGwqj1m3YEHHNvspSzY0o5HzA0A==} + svelte-headless-table@0.18.3: + resolution: {integrity: sha512-1zVnqXW0dvn6ZceYa94k+ziK+w5Dj9nlWYTQGXBv2JhM0resj9w7CWpclZK1TJwAALfEeH4InPBPO87L5fr+nQ==} peerDependencies: svelte: ^4.0.0 @@ -4777,8 +4808,8 @@ packages: '@sveltejs/kit': 1.x || 2.x svelte: 3.x || 4.x || >=5.0.0-next.51 - sveltekit-superforms@2.19.1: - resolution: {integrity: sha512-P3R3S8o+0UGHtVqmisb13aFVuIyTCsFdxh/2C/fvoR9/JKeBrhzJ/chI7GdByoXE5fr2DtanocGXmP3PRTcpvw==} + sveltekit-superforms@2.20.0: + resolution: {integrity: sha512-5HyA6THKFBHEmJinZ/klu2/0jYr9ElSaXMYc5EO9ptP3x1wQPWVXYl59sMcaSrIjWUlPpayGxVppCyu+x/o4WA==} peerDependencies: '@sveltejs/kit': 1.x || 2.x svelte: 3.x || 4.x || >=5.0.0-next.51 @@ -4800,8 +4831,8 @@ packages: peerDependencies: tailwindcss: '>=3.0.0 || insiders' - tailwindcss@3.4.13: - resolution: {integrity: sha512-KqjHOJKogOUt5Bs752ykCeiwvi0fKVkr5oqsFNt/8px/tA8scFPIlkygsf6jXrfCqGHz7VflA6+yytWuM+XhFw==} + tailwindcss@3.4.14: + resolution: {integrity: sha512-IcSvOcTRcUtQQ7ILQL5quRDg7Xs93PdJEk1ZLbhhvJc7uj/OAhYOnruEiwnGgBvUtaUAJ8/mhSw1o8L2jCiENA==} engines: {node: '>=14.0.0'} hasBin: true @@ -4896,8 +4927,11 @@ packages: tslib@2.7.0: resolution: {integrity: sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==} - tsx@4.19.1: - resolution: {integrity: sha512-0flMz1lh74BR4wOvBjuh9olbnwqCPc35OOlfyzHba0Dc+QNUeWX/Gq2YTbnwcWPO3BMd8fkzRVrHcsR+a7z7rA==} + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + tsx@4.19.2: + resolution: {integrity: sha512-pOUl6Vo2LUq/bSa8S5q7b91cgNSjctn9ugq/+Mvow99qW6x/UZYwzxy/3NmqoT66eHYfCVvFvACC58UBPFf28g==} engines: {node: '>=18.0.0'} hasBin: true @@ -4974,8 +5008,13 @@ packages: valibot@0.31.1: resolution: {integrity: sha512-2YYIhPrnVSz/gfT2/iXVTrSj92HwchCt9Cga/6hX4B26iCz9zkIsGTS0HjDYTZfTi1Un0X6aRvhBi1cfqs/i0Q==} - valibot@0.35.0: - resolution: {integrity: sha512-+i2aCRkReTrd5KBN/dW2BrPOvFnU5LXTV2xjZnjnqUIO8YUx6P2+MgRrkwF2FhkexgyKq/NIZdPdknhHf5A/Ww==} + valibot@0.41.0: + resolution: {integrity: sha512-igDBb8CTYr8YTQlOKgaN9nSS0Be7z+WRuaeYqGf3Cjz3aKmSnqEmYnkfVjzIuumGqfHpa3fLIvMEAfhrpqN8ng==} + peerDependencies: + typescript: '>=5' + peerDependenciesMeta: + typescript: + optional: true validator@13.12.0: resolution: {integrity: sha512-c1Q0mCiPlgdTVVVIJIrBuxNicYE+t/7oKeI9MWLj3fh/uq2Pxh/3eeWbVZ4OcGW1TUf53At0njHw5SMdA3tmMg==} @@ -4994,8 +5033,8 @@ packages: engines: {node: ^18.0.0 || >=20.0.0} hasBin: true - vite@5.4.9: - resolution: {integrity: sha512-20OVpJHh0PAM0oSOELa5GaZNWeDjcAvQjGXy2Uyr+Tp+/D2/Hdz6NLgpJLsarPTA2QJ6v8mX2P1ZfbsSKvdMkg==} + vite@5.4.10: + resolution: {integrity: sha512-1hvaPshuPUtxeQ0hsVH3Mud0ZanOLwVTneA1EgbAM5LhaZEqyPWGRQ7BtaMvUrTDeEaC8pxtj6a6jku3x4z6SQ==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true peerDependencies: @@ -5156,8 +5195,8 @@ packages: peerDependencies: zod: ^3.21.4 - zod-to-json-schema@3.23.3: - resolution: {integrity: sha512-TYWChTxKQbRJp5ST22o/Irt9KC5nj7CdBKYB/AosCRdj/wxEMvv4NNaj9XVUHDOIp53ZxArGhnw5HMZziPFjog==} + zod-to-json-schema@3.23.5: + resolution: {integrity: sha512-5wlSS0bXfF/BrL4jPAbz9da5hDlDptdEppYfe+x4eIJ7jioqKG9uUxOwPzqof09u/XeVdrgFu29lZi+8XNDJtA==} peerDependencies: zod: ^3.23.3 @@ -5468,32 +5507,38 @@ snapshots: dependencies: postcss: 8.4.47 - '@drizzle-team/brocli@0.8.2': {} + '@drizzle-team/brocli@0.10.2': {} + + '@effect/schema@0.75.5(effect@3.9.2)': + dependencies: + effect: 3.9.2 + fast-check: 3.22.0 + optional: true '@emnapi/core@0.45.0': dependencies: - tslib: 2.7.0 + tslib: 2.8.1 optional: true '@emnapi/core@1.2.0': dependencies: '@emnapi/wasi-threads': 1.0.1 - tslib: 2.7.0 + tslib: 2.8.1 optional: true '@emnapi/runtime@0.45.0': dependencies: - tslib: 2.7.0 + tslib: 2.8.1 optional: true '@emnapi/runtime@1.2.0': dependencies: - tslib: 2.7.0 + tslib: 2.8.1 optional: true '@emnapi/wasi-threads@1.0.1': dependencies: - tslib: 2.7.0 + tslib: 2.8.1 optional: true '@esbuild-kit/core-utils@3.3.2': @@ -5912,25 +5957,25 @@ snapshots: '@hapi/hoek': 9.3.0 optional: true - '@hono/swagger-ui@0.4.1(hono@4.6.4)': + '@hono/swagger-ui@0.4.1(hono@4.6.9)': dependencies: - hono: 4.6.4 + hono: 4.6.9 - '@hono/zod-openapi@0.15.3(hono@4.6.4)(zod@3.23.8)': + '@hono/zod-openapi@0.15.3(hono@4.6.9)(zod@3.23.8)': dependencies: '@asteasolutions/zod-to-openapi': 7.1.2(zod@3.23.8) - '@hono/zod-validator': 0.2.2(hono@4.6.4)(zod@3.23.8) - hono: 4.6.4 + '@hono/zod-validator': 0.2.2(hono@4.6.9)(zod@3.23.8) + hono: 4.6.9 zod: 3.23.8 - '@hono/zod-validator@0.2.2(hono@4.6.4)(zod@3.23.8)': + '@hono/zod-validator@0.2.2(hono@4.6.9)(zod@3.23.8)': dependencies: - hono: 4.6.4 + hono: 4.6.9 zod: 3.23.8 - '@hono/zod-validator@0.4.1(hono@4.6.4)(zod@3.23.8)': + '@hono/zod-validator@0.4.1(hono@4.6.9)(zod@3.23.8)': dependencies: - hono: 4.6.4 + hono: 4.6.9 zod: 3.23.8 '@humanwhocodes/config-array@0.13.0': @@ -6071,9 +6116,9 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.0 - '@lucia-auth/adapter-drizzle@1.1.0(drizzle-orm@0.32.2(@neondatabase/serverless@0.9.5)(@types/pg@8.11.10)(pg@8.13.0)(postgres@3.4.4))(lucia@3.2.0)': + '@lucia-auth/adapter-drizzle@1.1.0(drizzle-orm@0.36.0(@neondatabase/serverless@0.9.5)(@types/pg@8.11.10)(pg@8.13.1)(postgres@3.4.5))(lucia@3.2.0)': dependencies: - drizzle-orm: 0.32.2(@neondatabase/serverless@0.9.5)(@types/pg@8.11.10)(pg@8.13.0)(postgres@3.4.4) + drizzle-orm: 0.36.0(@neondatabase/serverless@0.9.5)(@types/pg@8.11.10)(pg@8.13.1)(postgres@3.4.5) lucia: 3.2.0 '@lukeed/csprng@1.1.0': {} @@ -6409,24 +6454,24 @@ snapshots: '@pkgjs/parseargs@0.11.0': optional: true - '@playwright/test@1.48.0': + '@playwright/test@1.48.2': dependencies: - playwright: 1.48.0 + playwright: 1.48.2 '@polka/url@1.0.0-next.28': {} '@poppinss/macroable@1.0.3': optional: true - '@rollup/plugin-commonjs@28.0.0(rollup@4.24.0)': + '@rollup/plugin-commonjs@28.0.1(rollup@4.24.0)': dependencies: '@rollup/pluginutils': 5.1.2(rollup@4.24.0) commondir: 1.0.1 estree-walker: 2.0.2 - fdir: 6.4.0(picomatch@2.3.1) + fdir: 6.4.2(picomatch@4.0.2) is-reference: 1.2.1 - magic-string: 0.30.11 - picomatch: 2.3.1 + magic-string: 0.30.12 + picomatch: 4.0.2 optionalDependencies: rollup: 4.24.0 @@ -6507,17 +6552,17 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.24.0': optional: true - '@scalar/hono-api-reference@0.5.154(hono@4.6.4)': + '@scalar/hono-api-reference@0.5.158(hono@4.6.9)': dependencies: - '@scalar/types': 0.0.15 - hono: 4.6.4 + '@scalar/types': 0.0.18 + hono: 4.6.9 - '@scalar/openapi-types@0.1.3': {} + '@scalar/openapi-types@0.1.4': {} - '@scalar/types@0.0.15': + '@scalar/types@0.0.18': dependencies: - '@scalar/openapi-types': 0.1.3 - '@unhead/schema': 1.11.9 + '@scalar/openapi-types': 0.1.4 + '@unhead/schema': 1.11.11 '@sideway/address@4.1.5': dependencies: @@ -6535,41 +6580,41 @@ snapshots: '@sinclair/typebox@0.32.35': optional: true - '@sveltejs/adapter-auto@3.2.5(@sveltejs/kit@2.7.1(@sveltejs/vite-plugin-svelte@4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.9(@types/node@20.16.11)))(svelte@5.0.0-next.175)(vite@5.4.9(@types/node@20.16.11)))': + '@sveltejs/adapter-auto@3.3.1(@sveltejs/kit@2.7.5(@sveltejs/vite-plugin-svelte@4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.10(@types/node@20.17.6)))(svelte@5.0.0-next.175)(vite@5.4.10(@types/node@20.17.6)))': dependencies: - '@sveltejs/kit': 2.7.1(@sveltejs/vite-plugin-svelte@4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.9(@types/node@20.16.11)))(svelte@5.0.0-next.175)(vite@5.4.9(@types/node@20.16.11)) + '@sveltejs/kit': 2.7.5(@sveltejs/vite-plugin-svelte@4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.10(@types/node@20.17.6)))(svelte@5.0.0-next.175)(vite@5.4.10(@types/node@20.17.6)) import-meta-resolve: 4.1.0 - '@sveltejs/adapter-node@5.2.7(@sveltejs/kit@2.7.1(@sveltejs/vite-plugin-svelte@4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.9(@types/node@20.16.11)))(svelte@5.0.0-next.175)(vite@5.4.9(@types/node@20.16.11)))': + '@sveltejs/adapter-node@5.2.9(@sveltejs/kit@2.7.5(@sveltejs/vite-plugin-svelte@4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.10(@types/node@20.17.6)))(svelte@5.0.0-next.175)(vite@5.4.10(@types/node@20.17.6)))': dependencies: - '@rollup/plugin-commonjs': 28.0.0(rollup@4.24.0) + '@rollup/plugin-commonjs': 28.0.1(rollup@4.24.0) '@rollup/plugin-json': 6.1.0(rollup@4.24.0) '@rollup/plugin-node-resolve': 15.3.0(rollup@4.24.0) - '@sveltejs/kit': 2.7.1(@sveltejs/vite-plugin-svelte@4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.9(@types/node@20.16.11)))(svelte@5.0.0-next.175)(vite@5.4.9(@types/node@20.16.11)) + '@sveltejs/kit': 2.7.5(@sveltejs/vite-plugin-svelte@4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.10(@types/node@20.17.6)))(svelte@5.0.0-next.175)(vite@5.4.10(@types/node@20.17.6)) rollup: 4.24.0 - '@sveltejs/adapter-vercel@5.4.5(@sveltejs/kit@2.7.1(@sveltejs/vite-plugin-svelte@4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.9(@types/node@20.16.11)))(svelte@5.0.0-next.175)(vite@5.4.9(@types/node@20.16.11)))': + '@sveltejs/adapter-vercel@5.4.6(@sveltejs/kit@2.7.5(@sveltejs/vite-plugin-svelte@4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.10(@types/node@20.17.6)))(svelte@5.0.0-next.175)(vite@5.4.10(@types/node@20.17.6)))': dependencies: - '@sveltejs/kit': 2.7.1(@sveltejs/vite-plugin-svelte@4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.9(@types/node@20.16.11)))(svelte@5.0.0-next.175)(vite@5.4.9(@types/node@20.16.11)) + '@sveltejs/kit': 2.7.5(@sveltejs/vite-plugin-svelte@4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.10(@types/node@20.17.6)))(svelte@5.0.0-next.175)(vite@5.4.10(@types/node@20.17.6)) '@vercel/nft': 0.27.4 esbuild: 0.21.5 transitivePeerDependencies: - encoding - supports-color - '@sveltejs/enhanced-img@0.3.9(rollup@4.24.0)(svelte@5.0.0-next.175)(vite@5.4.9(@types/node@20.16.11))': + '@sveltejs/enhanced-img@0.3.10(rollup@4.24.0)(svelte@5.0.0-next.175)(vite@5.4.10(@types/node@20.17.6))': dependencies: magic-string: 0.30.11 svelte: 5.0.0-next.175 svelte-parse-markup: 0.1.5(svelte@5.0.0-next.175) - vite: 5.4.9(@types/node@20.16.11) + vite: 5.4.10(@types/node@20.17.6) vite-imagetools: 7.0.4(rollup@4.24.0) transitivePeerDependencies: - rollup - '@sveltejs/kit@2.7.1(@sveltejs/vite-plugin-svelte@4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.9(@types/node@20.16.11)))(svelte@5.0.0-next.175)(vite@5.4.9(@types/node@20.16.11))': + '@sveltejs/kit@2.7.5(@sveltejs/vite-plugin-svelte@4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.10(@types/node@20.17.6)))(svelte@5.0.0-next.175)(vite@5.4.10(@types/node@20.17.6))': dependencies: - '@sveltejs/vite-plugin-svelte': 4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.9(@types/node@20.16.11)) + '@sveltejs/vite-plugin-svelte': 4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.10(@types/node@20.17.6)) '@types/cookie': 0.6.0 cookie: 0.6.0 devalue: 5.1.1 @@ -6583,33 +6628,33 @@ snapshots: sirv: 3.0.0 svelte: 5.0.0-next.175 tiny-glob: 0.2.9 - vite: 5.4.9(@types/node@20.16.11) + vite: 5.4.10(@types/node@20.17.6) - '@sveltejs/vite-plugin-svelte-inspector@3.0.0-next.3(@sveltejs/vite-plugin-svelte@4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.9(@types/node@20.16.11)))(svelte@5.0.0-next.175)(vite@5.4.9(@types/node@20.16.11))': + '@sveltejs/vite-plugin-svelte-inspector@3.0.0-next.3(@sveltejs/vite-plugin-svelte@4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.10(@types/node@20.17.6)))(svelte@5.0.0-next.175)(vite@5.4.10(@types/node@20.17.6))': dependencies: - '@sveltejs/vite-plugin-svelte': 4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.9(@types/node@20.16.11)) + '@sveltejs/vite-plugin-svelte': 4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.10(@types/node@20.17.6)) debug: 4.3.7 svelte: 5.0.0-next.175 - vite: 5.4.9(@types/node@20.16.11) + vite: 5.4.10(@types/node@20.17.6) transitivePeerDependencies: - supports-color - '@sveltejs/vite-plugin-svelte@4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.9(@types/node@20.16.11))': + '@sveltejs/vite-plugin-svelte@4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.10(@types/node@20.17.6))': dependencies: - '@sveltejs/vite-plugin-svelte-inspector': 3.0.0-next.3(@sveltejs/vite-plugin-svelte@4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.9(@types/node@20.16.11)))(svelte@5.0.0-next.175)(vite@5.4.9(@types/node@20.16.11)) + '@sveltejs/vite-plugin-svelte-inspector': 3.0.0-next.3(@sveltejs/vite-plugin-svelte@4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.10(@types/node@20.17.6)))(svelte@5.0.0-next.175)(vite@5.4.10(@types/node@20.17.6)) debug: 4.3.7 deepmerge: 4.3.1 kleur: 4.1.5 magic-string: 0.30.11 svelte: 5.0.0-next.175 - vite: 5.4.9(@types/node@20.16.11) - vitefu: 1.0.2(vite@5.4.9(@types/node@20.16.11)) + vite: 5.4.10(@types/node@20.17.6) + vitefu: 1.0.2(vite@5.4.10(@types/node@20.17.6)) transitivePeerDependencies: - supports-color '@swc/helpers@0.5.13': dependencies: - tslib: 2.7.0 + tslib: 2.8.1 '@tsconfig/node10@1.0.11': {} @@ -6621,12 +6666,12 @@ snapshots: '@tybys/wasm-util@0.8.3': dependencies: - tslib: 2.7.0 + tslib: 2.8.1 optional: true '@tybys/wasm-util@0.9.0': dependencies: - tslib: 2.7.0 + tslib: 2.8.1 optional: true '@types/cookie@0.6.0': {} @@ -6638,19 +6683,19 @@ snapshots: '@types/json-schema@7.0.15': optional: true - '@types/node@20.16.11': + '@types/node@20.17.6': dependencies: undici-types: 6.19.8 '@types/pg@8.11.10': dependencies: - '@types/node': 20.16.11 + '@types/node': 20.17.6 pg-protocol: 1.7.0 pg-types: 4.0.2 '@types/pg@8.11.6': dependencies: - '@types/node': 20.16.11 + '@types/node': 20.17.6 pg-protocol: 1.7.0 pg-types: 4.0.2 @@ -6658,14 +6703,14 @@ snapshots: '@types/qrcode@1.5.5': dependencies: - '@types/node': 20.16.11 + '@types/node': 20.17.6 '@types/resolve@1.20.2': {} '@types/validator@13.12.2': optional: true - '@typeschema/class-validator@0.2.0(@types/json-schema@7.0.15)(class-validator@0.14.1)': + '@typeschema/class-validator@0.3.0(@types/json-schema@7.0.15)(class-validator@0.14.1)': dependencies: '@typeschema/core': 0.14.0(@types/json-schema@7.0.15) optionalDependencies: @@ -6762,7 +6807,7 @@ snapshots: '@ungap/structured-clone@1.2.0': {} - '@unhead/schema@1.11.9': + '@unhead/schema@1.11.11': dependencies: hookable: 5.5.3 zhead: 2.2.4 @@ -7008,14 +7053,14 @@ snapshots: base64-js: 1.5.1 ieee754: 1.2.1 - bullmq@5.20.0: + bullmq@5.24.0: dependencies: cron-parser: 4.9.0 ioredis: 5.4.1 - msgpackr: 1.11.0 + msgpackr: 1.11.2 node-abort-controller: 3.1.1 semver: 7.6.3 - tslib: 2.7.0 + tslib: 2.8.1 uuid: 9.0.1 transitivePeerDependencies: - supports-color @@ -7149,6 +7194,8 @@ snapshots: cookie@0.6.0: {} + cookie@1.0.1: {} + core-js@3.38.1: {} create-require@1.1.1: {} @@ -7259,31 +7306,34 @@ snapshots: dotenv@16.4.5: {} - drizzle-kit@0.23.2: + drizzle-kit@0.27.1: dependencies: - '@drizzle-team/brocli': 0.8.2 + '@drizzle-team/brocli': 0.10.2 '@esbuild-kit/esm-loader': 2.6.5 esbuild: 0.19.12 esbuild-register: 3.6.0(esbuild@0.19.12) transitivePeerDependencies: - supports-color - drizzle-orm@0.32.2(@neondatabase/serverless@0.9.5)(@types/pg@8.11.10)(pg@8.13.0)(postgres@3.4.4): + drizzle-orm@0.36.0(@neondatabase/serverless@0.9.5)(@types/pg@8.11.10)(pg@8.13.1)(postgres@3.4.5): optionalDependencies: '@neondatabase/serverless': 0.9.5 '@types/pg': 8.11.10 - pg: 8.13.0 - postgres: 3.4.4 + pg: 8.13.1 + postgres: 3.4.5 - drizzle-zod@0.5.1(drizzle-orm@0.32.2(@neondatabase/serverless@0.9.5)(@types/pg@8.11.10)(pg@8.13.0)(postgres@3.4.4))(zod@3.23.8): + drizzle-zod@0.5.1(drizzle-orm@0.36.0(@neondatabase/serverless@0.9.5)(@types/pg@8.11.10)(pg@8.13.1)(postgres@3.4.5))(zod@3.23.8): dependencies: - drizzle-orm: 0.32.2(@neondatabase/serverless@0.9.5)(@types/pg@8.11.10)(pg@8.13.0)(postgres@3.4.4) + drizzle-orm: 0.36.0(@neondatabase/serverless@0.9.5)(@types/pg@8.11.10)(pg@8.13.1)(postgres@3.4.5) zod: 3.23.8 eastasianwidth@0.2.0: {} ee-first@1.1.1: {} + effect@3.9.2: + optional: true + electron-to-chromium@1.5.32: {} emoji-regex@8.0.0: {} @@ -7467,7 +7517,7 @@ snapshots: dependencies: eslint: 8.57.1 - eslint-plugin-svelte@2.36.0-next.13(eslint@8.57.1)(svelte@5.0.0-next.175)(ts-node@10.9.2(@types/node@20.16.11)(typescript@5.6.3)): + eslint-plugin-svelte@2.36.0-next.13(eslint@8.57.1)(svelte@5.0.0-next.175)(ts-node@10.9.2(@types/node@20.17.6)(typescript@5.6.3)): dependencies: '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.1) '@jridgewell/sourcemap-codec': 1.5.0 @@ -7477,7 +7527,7 @@ snapshots: esutils: 2.0.3 known-css-properties: 0.30.0 postcss: 8.4.47 - postcss-load-config: 3.1.4(postcss@8.4.47)(ts-node@10.9.2(@types/node@20.16.11)(typescript@5.6.3)) + postcss-load-config: 3.1.4(postcss@8.4.47)(ts-node@10.9.2(@types/node@20.17.6)(typescript@5.6.3)) postcss-safe-parser: 6.0.0(postcss@8.4.47) postcss-selector-parser: 6.1.2 semver: 7.6.3 @@ -7627,6 +7677,11 @@ snapshots: transitivePeerDependencies: - supports-color + fast-check@3.22.0: + dependencies: + pure-rand: 6.1.0 + optional: true + fast-copy@3.0.2: {} fast-deep-equal@3.1.3: {} @@ -7655,9 +7710,9 @@ snapshots: dependencies: reusify: 1.0.4 - fdir@6.4.0(picomatch@2.3.1): + fdir@6.4.2(picomatch@4.0.2): optionalDependencies: - picomatch: 2.3.1 + picomatch: 4.0.2 feather-icons@4.29.2: dependencies: @@ -7713,11 +7768,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.19.1(@sveltejs/kit@2.7.1(@sveltejs/vite-plugin-svelte@4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.9(@types/node@20.16.11)))(svelte@5.0.0-next.175)(vite@5.4.9(@types/node@20.16.11)))(@types/json-schema@7.0.15)(svelte@5.0.0-next.175)): + formsnap@1.0.1(svelte@5.0.0-next.175)(sveltekit-superforms@2.20.0(@sveltejs/kit@2.7.5(@sveltejs/vite-plugin-svelte@4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.10(@types/node@20.17.6)))(svelte@5.0.0-next.175)(vite@5.4.10(@types/node@20.17.6)))(@types/json-schema@7.0.15)(svelte@5.0.0-next.175)(typescript@5.6.3)): dependencies: nanoid: 5.0.7 svelte: 5.0.0-next.175 - sveltekit-superforms: 2.19.1(@sveltejs/kit@2.7.1(@sveltejs/vite-plugin-svelte@4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.9(@types/node@20.16.11)))(svelte@5.0.0-next.175)(vite@5.4.9(@types/node@20.16.11)))(@types/json-schema@7.0.15)(svelte@5.0.0-next.175) + sveltekit-superforms: 2.20.0(@sveltejs/kit@2.7.5(@sveltejs/vite-plugin-svelte@4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.10(@types/node@20.17.6)))(svelte@5.0.0-next.175)(vite@5.4.10(@types/node@20.17.6)))(@types/json-schema@7.0.15)(svelte@5.0.0-next.175)(typescript@5.6.3) forwarded@0.2.0: {} @@ -7850,24 +7905,24 @@ snapshots: help-me@5.0.0: {} - hono-pino@0.3.0(hono@4.6.4)(pino@9.4.0): + hono-pino@0.3.0(hono@4.6.9)(pino@9.5.0): dependencies: defu: 6.1.4 - hono: 4.6.4 - pino: 9.4.0 + hono: 4.6.9 + pino: 9.5.0 - hono-rate-limiter@0.4.0(hono@4.6.4): + hono-rate-limiter@0.4.0(hono@4.6.9): dependencies: - hono: 4.6.4 + hono: 4.6.9 - hono-zod-openapi@0.3.0(hono@4.6.4)(zod@3.23.8): + hono-zod-openapi@0.3.1(hono@4.6.9)(zod@3.23.8): dependencies: - '@hono/zod-validator': 0.4.1(hono@4.6.4)(zod@3.23.8) - hono: 4.6.4 + '@hono/zod-validator': 0.4.1(hono@4.6.9)(zod@3.23.8) + hono: 4.6.9 zod: 3.23.8 zod-openapi: 3.1.1(zod@3.23.8) - hono@4.6.4: {} + hono@4.6.9: {} hookable@5.5.3: {} @@ -8100,6 +8155,10 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.5.0 + magic-string@0.30.12: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.0 + make-dir@3.1.0: dependencies: semver: 6.3.1 @@ -8209,7 +8268,7 @@ snapshots: '@msgpackr-extract/msgpackr-extract-win32-x64': 3.0.3 optional: true - msgpackr@1.11.0: + msgpackr@1.11.2: optionalDependencies: msgpackr-extract: 3.0.3 @@ -8244,7 +8303,7 @@ snapshots: node-releases@2.0.18: {} - nodemailer@6.9.15: {} + nodemailer@6.9.16: {} nopt@5.0.0: dependencies: @@ -8383,9 +8442,9 @@ snapshots: pg-numeric@1.0.2: {} - pg-pool@3.7.0(pg@8.13.0): + pg-pool@3.7.0(pg@8.13.1): dependencies: - pg: 8.13.0 + pg: 8.13.1 pg-protocol@1.7.0: {} @@ -8407,10 +8466,10 @@ snapshots: postgres-interval: 3.0.0 postgres-range: 1.1.4 - pg@8.13.0: + pg@8.13.1: dependencies: pg-connection-string: 2.7.0 - pg-pool: 3.7.0(pg@8.13.0) + pg-pool: 3.7.0(pg@8.13.1) pg-protocol: 1.7.0 pg-types: 2.2.0 pgpass: 1.0.5 @@ -8425,14 +8484,15 @@ snapshots: picomatch@2.3.1: {} + picomatch@4.0.2: {} + pify@2.3.0: {} - pino-abstract-transport@1.2.0: + pino-abstract-transport@2.0.0: dependencies: - readable-stream: 4.5.2 split2: 4.2.0 - pino-pretty@11.2.2: + pino-pretty@11.3.0: dependencies: colorette: 2.0.20 dateformat: 4.6.3 @@ -8442,7 +8502,7 @@ snapshots: joycon: 3.1.1 minimist: 1.2.8 on-exit-leak-free: 2.1.2 - pino-abstract-transport: 1.2.0 + pino-abstract-transport: 2.0.0 pump: 3.0.2 readable-stream: 4.5.2 secure-json-parse: 2.7.0 @@ -8451,12 +8511,12 @@ snapshots: pino-std-serializers@7.0.0: {} - pino@9.4.0: + pino@9.5.0: dependencies: atomic-sleep: 1.0.0 fast-redact: 3.5.0 on-exit-leak-free: 2.1.2 - pino-abstract-transport: 1.2.0 + pino-abstract-transport: 2.0.0 pino-std-serializers: 7.0.0 process-warning: 4.0.0 quick-format-unescaped: 4.0.4 @@ -8473,11 +8533,11 @@ snapshots: mlly: 1.7.1 pathe: 1.1.2 - playwright-core@1.48.0: {} + playwright-core@1.48.2: {} - playwright@1.48.0: + playwright@1.48.2: dependencies: - playwright-core: 1.48.0 + playwright-core: 1.48.2 optionalDependencies: fsevents: 2.3.2 @@ -8603,30 +8663,30 @@ snapshots: '@csstools/utilities': 1.0.0(postcss@8.4.47) postcss: 8.4.47 - postcss-load-config@3.1.4(postcss@8.4.47)(ts-node@10.9.2(@types/node@20.16.11)(typescript@5.6.3)): + postcss-load-config@3.1.4(postcss@8.4.47)(ts-node@10.9.2(@types/node@20.17.6)(typescript@5.6.3)): dependencies: lilconfig: 2.1.0 yaml: 1.10.2 optionalDependencies: postcss: 8.4.47 - ts-node: 10.9.2(@types/node@20.16.11)(typescript@5.6.3) + ts-node: 10.9.2(@types/node@20.17.6)(typescript@5.6.3) - postcss-load-config@4.0.2(postcss@8.4.47)(ts-node@10.9.2(@types/node@20.16.11)(typescript@5.6.3)): + postcss-load-config@4.0.2(postcss@8.4.47)(ts-node@10.9.2(@types/node@20.17.6)(typescript@5.6.3)): dependencies: lilconfig: 3.1.2 yaml: 2.5.1 optionalDependencies: postcss: 8.4.47 - ts-node: 10.9.2(@types/node@20.16.11)(typescript@5.6.3) + ts-node: 10.9.2(@types/node@20.17.6)(typescript@5.6.3) - postcss-load-config@5.1.0(jiti@1.21.6)(postcss@8.4.47)(tsx@4.19.1): + postcss-load-config@5.1.0(jiti@1.21.6)(postcss@8.4.47)(tsx@4.19.2): dependencies: lilconfig: 3.1.2 yaml: 2.5.1 optionalDependencies: jiti: 1.21.6 postcss: 8.4.47 - tsx: 4.19.1 + tsx: 4.19.2 postcss-logical@7.0.1(postcss@8.4.47): dependencies: @@ -8785,7 +8845,7 @@ snapshots: postgres-range@1.1.4: {} - postgres@3.4.4: {} + postgres@3.4.5: {} prelude-ls@1.2.1: {} @@ -8821,6 +8881,9 @@ snapshots: punycode@2.3.1: {} + pure-rand@6.1.0: + optional: true + qrcode@1.5.4: dependencies: dijkstrajs: 1.0.3 @@ -9101,13 +9164,13 @@ snapshots: std-env@3.7.0: {} - stoker@1.2.3(@asteasolutions/zod-to-openapi@7.1.2(zod@3.23.8))(@hono/zod-openapi@0.15.3(hono@4.6.4)(zod@3.23.8))(hono@4.6.4)(openapi3-ts@4.4.0): + stoker@1.3.0(@asteasolutions/zod-to-openapi@7.1.2(zod@3.23.8))(@hono/zod-openapi@0.15.3(hono@4.6.9)(zod@3.23.8))(hono@4.6.9)(openapi3-ts@4.4.0): dependencies: '@asteasolutions/zod-to-openapi': 7.1.2(zod@3.23.8) - hono: 4.6.4 + hono: 4.6.9 openapi3-ts: 4.4.0 optionalDependencies: - '@hono/zod-openapi': 0.15.3(hono@4.6.4)(zod@3.23.8) + '@hono/zod-openapi': 0.15.3(hono@4.6.9)(zod@3.23.8) string-width@4.2.3: dependencies: @@ -9166,14 +9229,14 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} - svelte-check@3.8.6(postcss-load-config@5.1.0(jiti@1.21.6)(postcss@8.4.47)(tsx@4.19.1))(postcss@8.4.47)(svelte@5.0.0-next.175): + svelte-check@3.8.6(postcss-load-config@5.1.0(jiti@1.21.6)(postcss@8.4.47)(tsx@4.19.2))(postcss@8.4.47)(svelte@5.0.0-next.175): dependencies: '@jridgewell/trace-mapping': 0.3.25 chokidar: 3.6.0 picocolors: 1.1.0 sade: 1.8.1 svelte: 5.0.0-next.175 - svelte-preprocess: 5.1.4(postcss-load-config@5.1.0(jiti@1.21.6)(postcss@8.4.47)(tsx@4.19.1))(postcss@8.4.47)(svelte@5.0.0-next.175)(typescript@5.6.3) + svelte-preprocess: 5.1.4(postcss-load-config@5.1.0(jiti@1.21.6)(postcss@8.4.47)(tsx@4.19.2))(postcss@8.4.47)(svelte@5.0.0-next.175)(typescript@5.6.3) typescript: 5.6.3 transitivePeerDependencies: - '@babel/core' @@ -9196,7 +9259,7 @@ snapshots: optionalDependencies: svelte: 5.0.0-next.175 - svelte-headless-table@0.18.2(svelte@5.0.0-next.175): + svelte-headless-table@0.18.3(svelte@5.0.0-next.175): dependencies: svelte: 5.0.0-next.175 svelte-keyed: 2.0.0(svelte@5.0.0-next.175) @@ -9220,7 +9283,7 @@ snapshots: dependencies: svelte: 5.0.0-next.175 - svelte-preprocess@5.1.4(postcss-load-config@5.1.0(jiti@1.21.6)(postcss@8.4.47)(tsx@4.19.1))(postcss@8.4.47)(svelte@5.0.0-next.175)(typescript@5.6.3): + svelte-preprocess@5.1.4(postcss-load-config@5.1.0(jiti@1.21.6)(postcss@8.4.47)(tsx@4.19.2))(postcss@8.4.47)(svelte@5.0.0-next.175)(typescript@5.6.3): dependencies: '@types/pug': 2.0.10 detect-indent: 6.1.0 @@ -9230,15 +9293,15 @@ snapshots: svelte: 5.0.0-next.175 optionalDependencies: postcss: 8.4.47 - postcss-load-config: 5.1.0(jiti@1.21.6)(postcss@8.4.47)(tsx@4.19.1) + postcss-load-config: 5.1.0(jiti@1.21.6)(postcss@8.4.47)(tsx@4.19.2) typescript: 5.6.3 - svelte-preprocess@6.0.3(postcss-load-config@5.1.0(jiti@1.21.6)(postcss@8.4.47)(tsx@4.19.1))(postcss@8.4.47)(svelte@5.0.0-next.175)(typescript@5.6.3): + svelte-preprocess@6.0.3(postcss-load-config@5.1.0(jiti@1.21.6)(postcss@8.4.47)(tsx@4.19.2))(postcss@8.4.47)(svelte@5.0.0-next.175)(typescript@5.6.3): dependencies: svelte: 5.0.0-next.175 optionalDependencies: postcss: 8.4.47 - postcss-load-config: 5.1.0(jiti@1.21.6)(postcss@8.4.47)(tsx@4.19.1) + postcss-load-config: 5.1.0(jiti@1.21.6)(postcss@8.4.47)(tsx@4.19.2) typescript: 5.6.3 svelte-render@2.0.1(svelte@5.0.0-next.175): @@ -9292,51 +9355,54 @@ snapshots: magic-string: 0.30.11 zimmerframe: 1.1.2 - sveltekit-flash-message@2.4.4(@sveltejs/kit@2.7.1(@sveltejs/vite-plugin-svelte@4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.9(@types/node@20.16.11)))(svelte@5.0.0-next.175)(vite@5.4.9(@types/node@20.16.11)))(svelte@5.0.0-next.175): + sveltekit-flash-message@2.4.4(@sveltejs/kit@2.7.5(@sveltejs/vite-plugin-svelte@4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.10(@types/node@20.17.6)))(svelte@5.0.0-next.175)(vite@5.4.10(@types/node@20.17.6)))(svelte@5.0.0-next.175): dependencies: - '@sveltejs/kit': 2.7.1(@sveltejs/vite-plugin-svelte@4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.9(@types/node@20.16.11)))(svelte@5.0.0-next.175)(vite@5.4.9(@types/node@20.16.11)) + '@sveltejs/kit': 2.7.5(@sveltejs/vite-plugin-svelte@4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.10(@types/node@20.17.6)))(svelte@5.0.0-next.175)(vite@5.4.10(@types/node@20.17.6)) svelte: 5.0.0-next.175 - sveltekit-superforms@2.19.1(@sveltejs/kit@2.7.1(@sveltejs/vite-plugin-svelte@4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.9(@types/node@20.16.11)))(svelte@5.0.0-next.175)(vite@5.4.9(@types/node@20.16.11)))(@types/json-schema@7.0.15)(svelte@5.0.0-next.175): + sveltekit-superforms@2.20.0(@sveltejs/kit@2.7.5(@sveltejs/vite-plugin-svelte@4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.10(@types/node@20.17.6)))(svelte@5.0.0-next.175)(vite@5.4.10(@types/node@20.17.6)))(@types/json-schema@7.0.15)(svelte@5.0.0-next.175)(typescript@5.6.3): dependencies: - '@sveltejs/kit': 2.7.1(@sveltejs/vite-plugin-svelte@4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.9(@types/node@20.16.11)))(svelte@5.0.0-next.175)(vite@5.4.9(@types/node@20.16.11)) + '@sveltejs/kit': 2.7.5(@sveltejs/vite-plugin-svelte@4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.10(@types/node@20.17.6)))(svelte@5.0.0-next.175)(vite@5.4.10(@types/node@20.17.6)) devalue: 5.1.1 just-clone: 6.2.0 memoize-weak: 1.0.2 svelte: 5.0.0-next.175 ts-deepmerge: 7.0.1 optionalDependencies: + '@effect/schema': 0.75.5(effect@3.9.2) '@exodus/schemasafe': 1.3.0 '@gcornut/valibot-json-schema': 0.31.0 '@sinclair/typebox': 0.32.35 - '@typeschema/class-validator': 0.2.0(@types/json-schema@7.0.15)(class-validator@0.14.1) + '@typeschema/class-validator': 0.3.0(@types/json-schema@7.0.15)(class-validator@0.14.1) '@vinejs/vine': 1.8.0 arktype: 2.0.0-rc.8 class-validator: 0.14.1 + effect: 3.9.2 joi: 17.13.3 json-schema-to-ts: 3.1.1 superstruct: 2.0.2 - valibot: 0.35.0 + valibot: 0.41.0(typescript@5.6.3) yup: 1.4.0 zod: 3.23.8 - zod-to-json-schema: 3.23.3(zod@3.23.8) + zod-to-json-schema: 3.23.5(zod@3.23.8) transitivePeerDependencies: - '@types/json-schema' + - typescript tabbable@6.2.0: {} tailwind-merge@2.5.4: {} - tailwind-variants@0.2.1(tailwindcss@3.4.13(ts-node@10.9.2(@types/node@20.16.11)(typescript@5.6.3))): + tailwind-variants@0.2.1(tailwindcss@3.4.14(ts-node@10.9.2(@types/node@20.17.6)(typescript@5.6.3))): dependencies: tailwind-merge: 2.5.4 - tailwindcss: 3.4.13(ts-node@10.9.2(@types/node@20.16.11)(typescript@5.6.3)) + tailwindcss: 3.4.14(ts-node@10.9.2(@types/node@20.17.6)(typescript@5.6.3)) - tailwindcss-animate@1.0.7(tailwindcss@3.4.13(ts-node@10.9.2(@types/node@20.16.11)(typescript@5.6.3))): + tailwindcss-animate@1.0.7(tailwindcss@3.4.14(ts-node@10.9.2(@types/node@20.17.6)(typescript@5.6.3))): dependencies: - tailwindcss: 3.4.13(ts-node@10.9.2(@types/node@20.16.11)(typescript@5.6.3)) + tailwindcss: 3.4.14(ts-node@10.9.2(@types/node@20.17.6)(typescript@5.6.3)) - tailwindcss@3.4.13(ts-node@10.9.2(@types/node@20.16.11)(typescript@5.6.3)): + tailwindcss@3.4.14(ts-node@10.9.2(@types/node@20.17.6)(typescript@5.6.3)): dependencies: '@alloc/quick-lru': 5.2.0 arg: 5.0.2 @@ -9355,7 +9421,7 @@ snapshots: postcss: 8.4.47 postcss-import: 15.1.0(postcss@8.4.47) postcss-js: 4.0.1(postcss@8.4.47) - postcss-load-config: 4.0.2(postcss@8.4.47)(ts-node@10.9.2(@types/node@20.16.11)(typescript@5.6.3)) + postcss-load-config: 4.0.2(postcss@8.4.47)(ts-node@10.9.2(@types/node@20.17.6)(typescript@5.6.3)) postcss-nested: 6.2.0(postcss@8.4.47) postcss-selector-parser: 6.1.2 resolve: 1.22.8 @@ -9424,14 +9490,14 @@ snapshots: ts-interface-checker@0.1.13: {} - ts-node@10.9.2(@types/node@20.16.11)(typescript@5.6.3): + ts-node@10.9.2(@types/node@20.17.6)(typescript@5.6.3): 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.11 + '@types/node': 20.17.6 acorn: 8.12.1 acorn-walk: 8.3.4 arg: 4.1.3 @@ -9449,7 +9515,9 @@ snapshots: tslib@2.7.0: {} - tsx@4.19.1: + tslib@2.8.1: {} + + tsx@4.19.2: dependencies: esbuild: 0.23.1 get-tsconfig: 4.8.1 @@ -9510,7 +9578,9 @@ snapshots: valibot@0.31.1: optional: true - valibot@0.35.0: + valibot@0.41.0(typescript@5.6.3): + optionalDependencies: + typescript: 5.6.3 optional: true validator@13.12.0: @@ -9526,13 +9596,13 @@ snapshots: transitivePeerDependencies: - rollup - vite-node@1.6.0(@types/node@20.16.11): + vite-node@1.6.0(@types/node@20.17.6): dependencies: cac: 6.7.14 debug: 4.3.7 pathe: 1.1.2 picocolors: 1.1.0 - vite: 5.4.9(@types/node@20.16.11) + vite: 5.4.10(@types/node@20.17.6) transitivePeerDependencies: - '@types/node' - less @@ -9544,20 +9614,20 @@ snapshots: - supports-color - terser - vite@5.4.9(@types/node@20.16.11): + vite@5.4.10(@types/node@20.17.6): dependencies: esbuild: 0.21.5 postcss: 8.4.47 rollup: 4.24.0 optionalDependencies: - '@types/node': 20.16.11 + '@types/node': 20.17.6 fsevents: 2.3.3 - vitefu@1.0.2(vite@5.4.9(@types/node@20.16.11)): + vitefu@1.0.2(vite@5.4.10(@types/node@20.17.6)): optionalDependencies: - vite: 5.4.9(@types/node@20.16.11) + vite: 5.4.10(@types/node@20.17.6) - vitest@1.6.0(@types/node@20.16.11): + vitest@1.6.0(@types/node@20.17.6): dependencies: '@vitest/expect': 1.6.0 '@vitest/runner': 1.6.0 @@ -9576,11 +9646,11 @@ snapshots: strip-literal: 2.1.0 tinybench: 2.9.0 tinypool: 0.8.4 - vite: 5.4.9(@types/node@20.16.11) - vite-node: 1.6.0(@types/node@20.16.11) + vite: 5.4.10(@types/node@20.17.6) + vite-node: 1.6.0(@types/node@20.17.6) why-is-node-running: 2.3.0 optionalDependencies: - '@types/node': 20.16.11 + '@types/node': 20.17.6 transitivePeerDependencies: - less - lightningcss @@ -9688,7 +9758,7 @@ snapshots: dependencies: zod: 3.23.8 - zod-to-json-schema@3.23.3(zod@3.23.8): + zod-to-json-schema@3.23.5(zod@3.23.8): dependencies: zod: 3.23.8 diff --git a/src/lib/server/api/common/config.ts b/src/lib/server/api/common/config.ts index 083e0b6..f623262 100644 --- a/src/lib/server/api/common/config.ts +++ b/src/lib/server/api/common/config.ts @@ -18,5 +18,7 @@ export const config: Config = { database: env.DATABASE_DB, ssl: false, // env.DATABASE_HOST !== 'localhost', max: env.DB_MIGRATING || env.DB_SEEDING ? 1 : undefined, + migrating: env.DB_MIGRATING, + seeding: env.DB_SEEDING, }, }; diff --git a/src/lib/server/api/common/types/config.ts b/src/lib/server/api/common/types/config.ts index 1d7e7c2..c746233 100644 --- a/src/lib/server/api/common/types/config.ts +++ b/src/lib/server/api/common/types/config.ts @@ -1,14 +1,14 @@ export interface Config { - isProduction: boolean - domain: string - api: ApiConfig + isProduction: boolean; + domain: string; + api: ApiConfig; // storage: StorageConfig - redis: RedisConfig - postgres: PostgresConfig + redis: RedisConfig; + postgres: PostgresConfig; } interface ApiConfig { - origin: string + origin: string; } // interface StorageConfig { @@ -19,15 +19,17 @@ interface ApiConfig { // } interface RedisConfig { - url: string + url: string; } interface PostgresConfig { - user: string - password: string - host: string - port: number - database: string - ssl: boolean - max: number | undefined + user: string; + password: string; + host: string; + port: number; + database: string; + ssl: boolean; + max: number | undefined; + migrating: boolean; + seeding: boolean; } diff --git a/src/lib/server/api/common/utils/cookies.ts b/src/lib/server/api/common/utils/cookies.ts new file mode 100644 index 0000000..6e0f4c1 --- /dev/null +++ b/src/lib/server/api/common/utils/cookies.ts @@ -0,0 +1,34 @@ +import { config } from '$lib/server/api/common/config'; +import env from '$lib/server/api/common/env'; + +export function createSessionTokenCookie(token: string, expiresAt: Date) { + return { + name: 'session', + value: token, + attributes: { + path: '/', + maxAge: 60 * 60 * 24 * 30, + domain: env.DOMAIN, + sameSite: 'lax', + secure: config.isProduction, + httpOnly: true, + expires: expiresAt, + }, + }; +} + +export function createBlankSessionTokenCookie() { + return { + name: 'session', + value: '', + attributes: { + path: '/', + maxAge: 0, + domain: env.DOMAIN, + sameSite: 'lax', + secure: config.isProduction, + httpOnly: true, + expires: new Date(0), + }, + }; +} diff --git a/src/lib/server/api/common/utils/table.ts b/src/lib/server/api/common/utils/table.ts index a9fe09d..c9ba76d 100644 --- a/src/lib/server/api/common/utils/table.ts +++ b/src/lib/server/api/common/utils/table.ts @@ -1,17 +1,17 @@ -import { timestamp } from 'drizzle-orm/pg-core' -import { customType } from 'drizzle-orm/pg-core' +import { timestamp } from 'drizzle-orm/pg-core'; +import { customType } from 'drizzle-orm/pg-core'; export const citext = customType<{ data: string }>({ dataType() { - return 'citext' + return 'citext'; }, -}) +}); export const cuid2 = customType<{ data: string }>({ dataType() { - return 'text' + return 'text'; }, -}) +}); export const timestamps = { createdAt: timestamp('created_at', { @@ -25,5 +25,6 @@ export const timestamps = { withTimezone: true, }) .notNull() - .defaultNow(), -} + .defaultNow() + .$onUpdate(() => new Date()), +}; diff --git a/src/lib/server/api/controllers/collection.routes.ts b/src/lib/server/api/controllers/collection.routes.ts index e3ee453..f2d6b78 100644 --- a/src/lib/server/api/controllers/collection.routes.ts +++ b/src/lib/server/api/controllers/collection.routes.ts @@ -1,11 +1,11 @@ import { StatusCodes } from '$lib/constants/status-codes'; import { unauthorizedSchema } from '$lib/server/api/common/exceptions'; import cuidParamsSchema from '$lib/server/api/common/openapi/cuidParamsSchema'; -import { selectCollectionSchema } from '$lib/server/api/databases/tables'; import { z } from '@hono/zod-openapi'; import { IdParamsSchema } from 'stoker/openapi/schemas'; import { createErrorSchema } from 'stoker/openapi/schemas'; import { taggedAuthRoute } from '../common/openapi/create-auth-route'; +import { selectCollectionSchema } from '../databases/postgres/tables'; const tag = 'Collection'; diff --git a/src/lib/server/api/controllers/iam.controller.ts b/src/lib/server/api/controllers/iam.controller.ts index f907f82..e058992 100644 --- a/src/lib/server/api/controllers/iam.controller.ts +++ b/src/lib/server/api/controllers/iam.controller.ts @@ -1,5 +1,6 @@ import { StatusCodes } from '$lib/constants/status-codes'; import { Controller } from '$lib/server/api/common/types/controller'; +import { deleteSessionTokenCookie } from '$lib/server/api/common/utils/cookies'; import { changePasswordDto } from '$lib/server/api/dtos/change-password.dto'; import { updateEmailDto } from '$lib/server/api/dtos/update-email.dto'; import { updateProfileDto } from '$lib/server/api/dtos/update-profile.dto'; @@ -7,7 +8,7 @@ import { verifyPasswordDto } from '$lib/server/api/dtos/verify-password.dto'; import { limiter } from '$lib/server/api/middleware/rate-limiter.middleware'; import { IamService } from '$lib/server/api/services/iam.service'; import { LoginRequestsService } from '$lib/server/api/services/loginrequest.service'; -import { LuciaService } from '$lib/server/api/services/lucia.service'; +import { SessionsService } from '$lib/server/api/services/sessions.service'; import { zValidator } from '@hono/zod-validator'; import { openApi } from 'hono-zod-openapi'; import { setCookie } from 'hono/cookie'; @@ -20,7 +21,7 @@ export class IamController extends Controller { constructor( @inject(IamService) private readonly iamService: IamService, @inject(LoginRequestsService) private readonly loginRequestService: LoginRequestsService, - @inject(LuciaService) private luciaService: LuciaService, + @inject(SessionsService) private sessionsService: SessionsService, ) { super(); } @@ -78,18 +79,9 @@ export class IamController extends Controller { } try { await this.iamService.updatePassword(user.id, { password, confirm_password }); - await this.luciaService.lucia.invalidateUserSessions(user.id); + await this.sessionsService.invalidateSession(user.id); await this.loginRequestService.createUserSession(user.id, c.req, undefined); - const sessionCookie = this.luciaService.lucia.createBlankSessionCookie(); - setCookie(c, sessionCookie.name, sessionCookie.value, { - path: sessionCookie.attributes.path, - maxAge: sessionCookie.attributes.maxAge, - domain: sessionCookie.attributes.domain, - sameSite: sessionCookie.attributes.sameSite as any, - secure: sessionCookie.attributes.secure, - httpOnly: sessionCookie.attributes.httpOnly, - expires: sessionCookie.attributes.expires, - }); + deleteSessionTokenCookie(c); return c.json({ status: 'success' }); } catch (error) { console.error('Error updating password', error); @@ -116,16 +108,7 @@ export class IamController extends Controller { .post('/logout', requireAuth, openApi(logout), async (c) => { const sessionId = c.var.session.id; await this.iamService.logout(sessionId); - const sessionCookie = this.luciaService.lucia.createBlankSessionCookie(); - setCookie(c, sessionCookie.name, sessionCookie.value, { - path: sessionCookie.attributes.path, - maxAge: sessionCookie.attributes.maxAge, - domain: sessionCookie.attributes.domain, - sameSite: sessionCookie.attributes.sameSite as any, - secure: sessionCookie.attributes.secure, - httpOnly: sessionCookie.attributes.httpOnly, - expires: sessionCookie.attributes.expires, - }); + deleteSessionTokenCookie(c); return c.json({ status: 'success' }); }); } diff --git a/src/lib/server/api/controllers/iam.routes.ts b/src/lib/server/api/controllers/iam.routes.ts index be40ab9..b5a9adb 100644 --- a/src/lib/server/api/controllers/iam.routes.ts +++ b/src/lib/server/api/controllers/iam.routes.ts @@ -1,6 +1,6 @@ import { StatusCodes } from '$lib/constants/status-codes'; import { unauthorizedSchema } from '$lib/server/api/common/exceptions'; -import { selectUserSchema } from '$lib/server/api/databases/tables/users.table'; +import { selectUserSchema } from '$lib/server/api/databases/postgres/tables/users.table'; import { updateProfileDto } from '$lib/server/api/dtos/update-profile.dto'; import { createErrorSchema } from 'stoker/openapi/schemas'; import { taggedAuthRoute } from '../common/openapi/create-auth-route'; diff --git a/src/lib/server/api/controllers/login.controller.ts b/src/lib/server/api/controllers/login.controller.ts index 37d32ed..e04e3dd 100644 --- a/src/lib/server/api/controllers/login.controller.ts +++ b/src/lib/server/api/controllers/login.controller.ts @@ -1,44 +1,50 @@ -import 'reflect-metadata' -import { Controller } from '$lib/server/api/common/types/controller' -import { signinUsernameDto } from '$lib/server/api/dtos/signin-username.dto' -import { LuciaService } from '$lib/server/api/services/lucia.service' -import { zValidator } from '@hono/zod-validator' -import { setCookie } from 'hono/cookie' -import { TimeSpan } from 'oslo' +import 'reflect-metadata'; +import { Controller } from '$lib/server/api/common/types/controller'; +import { signinUsernameDto } from '$lib/server/api/dtos/signin-username.dto'; +import { SessionsService } from '$lib/server/api/services/sessions.service'; +import { zValidator } from '@hono/zod-validator'; import { openApi } from 'hono-zod-openapi'; -import { inject, injectable } from 'tsyringe' -import { limiter } from '../middleware/rate-limiter.middleware' -import { LoginRequestsService } from '../services/loginrequest.service' -import { signinUsername } from './login.routes' +import { setCookie } from 'hono/cookie'; +import { TimeSpan } from 'oslo'; +import { inject, injectable } from 'tsyringe'; +import { limiter } from '../middleware/rate-limiter.middleware'; +import { LoginRequestsService } from '../services/loginrequest.service'; +import { signinUsername } from './login.routes'; @injectable() export class LoginController extends Controller { constructor( @inject(LoginRequestsService) private readonly loginRequestsService: LoginRequestsService, - @inject(LuciaService) private luciaService: LuciaService, + @inject(SessionsService) private luciaService: SessionsService, ) { - super() + super(); } routes() { - return this.controller.post('/', openApi(signinUsername), zValidator('json', signinUsernameDto), limiter({ limit: 10, minutes: 60 }), async (c) => { - const { username, password } = c.req.valid('json') - const session = await this.loginRequestsService.verify({ username, password }, c.req) - const sessionCookie = this.luciaService.lucia.createSessionCookie(session.id) - console.log('set cookie', sessionCookie) - setCookie(c, sessionCookie.name, sessionCookie.value, { - path: sessionCookie.attributes.path, - maxAge: - sessionCookie?.attributes?.maxAge && sessionCookie?.attributes?.maxAge < new TimeSpan(365, 'd').seconds() - ? sessionCookie.attributes.maxAge - : new TimeSpan(2, 'w').seconds(), - domain: sessionCookie.attributes.domain, - sameSite: sessionCookie.attributes.sameSite as any, - secure: sessionCookie.attributes.secure, - httpOnly: sessionCookie.attributes.httpOnly, - expires: sessionCookie.attributes.expires, - }) - return c.json({ message: 'ok' }) - }) + return this.controller.post( + '/', + openApi(signinUsername), + zValidator('json', signinUsernameDto), + limiter({ limit: 10, minutes: 60 }), + async (c) => { + const { username, password } = c.req.valid('json'); + const session = await this.loginRequestsService.verify({ username, password }, c.req); + const sessionCookie = this.luciaService.lucia.createSessionCookie(session.id); + console.log('set cookie', sessionCookie); + setCookie(c, sessionCookie.name, sessionCookie.value, { + path: sessionCookie.attributes.path, + maxAge: + sessionCookie?.attributes?.maxAge && sessionCookie?.attributes?.maxAge < new TimeSpan(365, 'd').seconds() + ? sessionCookie.attributes.maxAge + : new TimeSpan(2, 'w').seconds(), + domain: sessionCookie.attributes.domain, + sameSite: sessionCookie.attributes.sameSite as any, + secure: sessionCookie.attributes.secure, + httpOnly: sessionCookie.attributes.httpOnly, + expires: sessionCookie.attributes.expires, + }); + return c.json({ message: 'ok' }); + }, + ); } } diff --git a/src/lib/server/api/controllers/mfa.controller.ts b/src/lib/server/api/controllers/mfa.controller.ts index b08b8c1..1258a10 100644 --- a/src/lib/server/api/controllers/mfa.controller.ts +++ b/src/lib/server/api/controllers/mfa.controller.ts @@ -1,14 +1,14 @@ -import 'reflect-metadata' -import { StatusCodes } from '$lib/constants/status-codes' -import { Controller } from '$lib/server/api/common/types/controller' -import { verifyTotpDto } from '$lib/server/api/dtos/verify-totp.dto' -import { RecoveryCodesService } from '$lib/server/api/services/recovery-codes.service' -import { TotpService } from '$lib/server/api/services/totp.service' -import { UsersService } from '$lib/server/api/services/users.service' -import { zValidator } from '@hono/zod-validator' -import { inject, injectable } from 'tsyringe' -import { CredentialsType } from '../databases/tables' -import { requireAuth } from '../middleware/require-auth.middleware' +import 'reflect-metadata'; +import { StatusCodes } from '$lib/constants/status-codes'; +import { Controller } from '$lib/server/api/common/types/controller'; +import { verifyTotpDto } from '$lib/server/api/dtos/verify-totp.dto'; +import { RecoveryCodesService } from '$lib/server/api/services/recovery-codes.service'; +import { TotpService } from '$lib/server/api/services/totp.service'; +import { UsersService } from '$lib/server/api/services/users.service'; +import { zValidator } from '@hono/zod-validator'; +import { inject, injectable } from 'tsyringe'; +import { CredentialsType } from '../databases/postgres/tables'; +import { requireAuth } from '../middleware/require-auth.middleware'; @injectable() export class MfaController extends Controller { @@ -17,59 +17,59 @@ export class MfaController extends Controller { @inject(TotpService) private readonly totpService: TotpService, @inject(UsersService) private readonly usersService: UsersService, ) { - super() + super(); } 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) - return c.json({ totpCredential }) + 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 + const user = c.var.user; try { - await this.totpService.deleteOneByUserIdAndType(user.id, CredentialsType.TOTP) - await this.recoveryCodesService.deleteAllRecoveryCodesByUserId(user.id) - await this.usersService.updateUser(user.id, { mfa_enabled: false }) - console.log('TOTP deleted') - return c.body(null, StatusCodes.NO_CONTENT) + await this.totpService.deleteOneByUserIdAndType(user.id, CredentialsType.TOTP); + await this.recoveryCodesService.deleteAllRecoveryCodesByUserId(user.id); + await this.usersService.updateUser(user.id, { mfa_enabled: false }); + console.log('TOTP deleted'); + return c.body(null, StatusCodes.NO_CONTENT); } catch (e) { - console.error(e) - return c.status(StatusCodes.INTERNAL_SERVER_ERROR) + console.error(e); + return c.status(StatusCodes.INTERNAL_SERVER_ERROR); } }) .get('/totp/recoveryCodes', requireAuth, async (c) => { - const user = c.var.user + const user = c.var.user; // You can only view recovery codes once and that is on creation - const existingCodes = await this.recoveryCodesService.findAllRecoveryCodesByUserId(user.id) + const existingCodes = await this.recoveryCodesService.findAllRecoveryCodesByUserId(user.id); if (existingCodes && existingCodes.length > 0) { - console.log('Recovery Codes found', existingCodes) - return c.json({ recoveryCodes: existingCodes }) + console.log('Recovery Codes found', existingCodes); + return c.json({ recoveryCodes: existingCodes }); } - const recoveryCodes = await this.recoveryCodesService.createRecoveryCodes(user.id) - return c.json({ recoveryCodes }) + const recoveryCodes = await this.recoveryCodesService.createRecoveryCodes(user.id); + return c.json({ recoveryCodes }); }) .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) + const user = c.var.user; + const { code } = c.req.valid('json'); + const verified = await this.totpService.verify(user.id, code); if (verified) { - await this.usersService.updateUser(user.id, { mfa_enabled: true }) - return c.json({}, StatusCodes.OK) + await this.usersService.updateUser(user.id, { mfa_enabled: true }); + return c.json({}, StatusCodes.OK); } - return c.json('Invalid code', StatusCodes.BAD_REQUEST) + return c.json('Invalid code', StatusCodes.BAD_REQUEST); } catch (e) { - console.error(e) - return c.status(StatusCodes.INTERNAL_SERVER_ERROR) + console.error(e); + return c.status(StatusCodes.INTERNAL_SERVER_ERROR); } - }) + }); } } diff --git a/src/lib/server/api/controllers/oauth.controller.ts b/src/lib/server/api/controllers/oauth.controller.ts index 6a542d0..3a4f3a5 100644 --- a/src/lib/server/api/controllers/oauth.controller.ts +++ b/src/lib/server/api/controllers/oauth.controller.ts @@ -1,53 +1,56 @@ -import 'reflect-metadata' -import { Controller } from '$lib/server/api/common/types/controller' -import { LuciaService } from '$lib/server/api/services/lucia.service' -import { OAuthService } from '$lib/server/api/services/oauth.service' -import { github, google } from '$lib/server/auth' -import { OAuth2RequestError } from 'arctic' -import { getCookie, setCookie } from 'hono/cookie' -import { TimeSpan } from 'oslo' -import { inject, injectable } from 'tsyringe' -import type {OAuthUser} from "$lib/server/api/common/types/oauth"; +import 'reflect-metadata'; +import { Controller } from '$lib/server/api/common/types/controller'; +import type { OAuthUser } from '$lib/server/api/common/types/oauth'; +import { createSessionTokenCookie } from '$lib/server/api/common/utils/cookies'; +import { OAuthService } from '$lib/server/api/services/oauth.service'; +import { SessionsService } from '$lib/server/api/services/sessions.service'; +import { github, google } from '$lib/server/auth'; +import { OAuth2RequestError } from 'arctic'; +import { getCookie, setCookie } from 'hono/cookie'; +import { TimeSpan } from 'oslo'; +import { inject, injectable } from 'tsyringe'; @injectable() export class OAuthController extends Controller { constructor( - @inject(LuciaService) private luciaService: LuciaService, + @inject(SessionsService) private sessionsService: SessionsService, @inject(OAuthService) private oauthService: OAuthService, ) { - super() + super(); } routes() { return this.controller .get('/github', async (c) => { try { - const code = c.req.query('code')?.toString() ?? null - const state = c.req.query('state')?.toString() ?? null - const storedState = getCookie(c).github_oauth_state ?? null + const code = c.req.query('code')?.toString() ?? null; + const state = c.req.query('state')?.toString() ?? null; + const storedState = getCookie(c).github_oauth_state ?? null; if (!code || !state || !storedState || state !== storedState) { - return c.body(null, 400) + return c.body(null, 400); } - const tokens = await github.validateAuthorizationCode(code) + const tokens = await github.validateAuthorizationCode(code); const githubUserResponse = await fetch('https://api.github.com/user', { headers: { Authorization: `Bearer ${tokens.accessToken}`, }, - }) - const githubUser: GitHubUser = await githubUserResponse.json() + }); + const githubUser: GitHubUser = await githubUserResponse.json(); const oAuthUser: OAuthUser = { sub: `${githubUser.id}`, username: githubUser.login, - email: undefined - } + email: undefined, + }; - const userId = await this.oauthService.handleOAuthUser(oAuthUser, 'github') + const userId = await this.oauthService.handleOAuthUser(oAuthUser, 'github'); - const session = await this.luciaService.lucia.createSession(userId, {}) - const sessionCookie = this.luciaService.lucia.createSessionCookie(session.id) + const sessionToken = this.sessionsService.generateSessionToken(); + const session = await this.sessionsService.createSession(sessionToken, userId, + req.); + const sessionCookie = createSessionTokenCookie(session.id, new Date(new TimeSpan(2, 'w').milliseconds())); setCookie(c, sessionCookie.name, sessionCookie.value, { path: sessionCookie.attributes.path, @@ -60,37 +63,37 @@ export class OAuthController extends Controller { secure: sessionCookie.attributes.secure, httpOnly: sessionCookie.attributes.httpOnly, expires: sessionCookie.attributes.expires, - }) + }); - return c.json({ message: 'ok' }) + return c.json({ message: 'ok' }); } catch (error) { - console.error(error) + console.error(error); // the specific error message depends on the provider if (error instanceof OAuth2RequestError) { // invalid code - return c.body(null, 400) + return c.body(null, 400); } - return c.body(null, 500) + return c.body(null, 500); } }) .get('/google', async (c) => { try { - const code = c.req.query('code')?.toString() ?? null - const state = c.req.query('state')?.toString() ?? null - const storedState = getCookie(c).google_oauth_state ?? null - const storedCodeVerifier = getCookie(c).google_oauth_code_verifier ?? null + const code = c.req.query('code')?.toString() ?? null; + const state = c.req.query('state')?.toString() ?? null; + const storedState = getCookie(c).google_oauth_state ?? null; + const storedCodeVerifier = getCookie(c).google_oauth_code_verifier ?? null; if (!code || !storedState || !storedCodeVerifier || state !== storedState) { - return c.body(null, 400) + return c.body(null, 400); } - const tokens = await google.validateAuthorizationCode(code, storedCodeVerifier) - const googleUserResponse = await fetch("https://openidconnect.googleapis.com/v1/userinfo", { + const tokens = await google.validateAuthorizationCode(code, storedCodeVerifier); + const googleUserResponse = await fetch('https://openidconnect.googleapis.com/v1/userinfo', { headers: { Authorization: `Bearer ${tokens.accessToken}`, }, - }) - const googleUser: GoogleUser = await googleUserResponse.json() + }); + const googleUser: GoogleUser = await googleUserResponse.json(); const oAuthUser: OAuthUser = { sub: googleUser.sub, @@ -100,12 +103,12 @@ export class OAuthController extends Controller { username: googleUser.email, email: googleUser.email, email_verified: googleUser.email_verified, - } + }; - const userId = await this.oauthService.handleOAuthUser(oAuthUser, 'google') + const userId = await this.oauthService.handleOAuthUser(oAuthUser, 'google'); - const session = await this.luciaService.lucia.createSession(userId, {}) - const sessionCookie = this.luciaService.lucia.createSessionCookie(session.id) + const session = await this.luciaService.lucia.createSession(userId, {}); + const sessionCookie = this.luciaService.lucia.createSessionCookie(session.id); setCookie(c, sessionCookie.name, sessionCookie.value, { path: sessionCookie.attributes.path, @@ -118,33 +121,33 @@ export class OAuthController extends Controller { secure: sessionCookie.attributes.secure, httpOnly: sessionCookie.attributes.httpOnly, expires: sessionCookie.attributes.expires, - }) + }); - return c.json({ message: 'ok' }) + return c.json({ message: 'ok' }); } catch (error) { - console.error(error) + console.error(error); // the specific error message depends on the provider if (error instanceof OAuth2RequestError) { // invalid code - return c.body(null, 400) + return c.body(null, 400); } - return c.body(null, 500) + return c.body(null, 500); } - }) + }); } } interface GitHubUser { - id: number - login: string + id: number; + login: string; } interface GoogleUser { - sub: string - name: string - given_name: string - family_name: string - picture: string - email: string - email_verified: boolean + sub: string; + name: string; + given_name: string; + family_name: string; + picture: string; + email: string; + email_verified: boolean; } diff --git a/src/lib/server/api/controllers/signup.controller.ts b/src/lib/server/api/controllers/signup.controller.ts index 40b1699..b2d5d3c 100644 --- a/src/lib/server/api/controllers/signup.controller.ts +++ b/src/lib/server/api/controllers/signup.controller.ts @@ -1,43 +1,43 @@ -import 'reflect-metadata' -import { Controller } from '$lib/server/api/common/types/controller' -import { signupUsernameEmailDto } from '$lib/server/api/dtos/signup-username-email.dto' -import { limiter } from '$lib/server/api/middleware/rate-limiter.middleware' -import { LoginRequestsService } from '$lib/server/api/services/loginrequest.service' -import { LuciaService } from '$lib/server/api/services/lucia.service' -import { UsersService } from '$lib/server/api/services/users.service' -import { zValidator } from '@hono/zod-validator' -import { setCookie } from 'hono/cookie' -import { TimeSpan } from 'oslo' -import { inject, injectable } from 'tsyringe' +import 'reflect-metadata'; +import { Controller } from '$lib/server/api/common/types/controller'; +import { signupUsernameEmailDto } from '$lib/server/api/dtos/signup-username-email.dto'; +import { limiter } from '$lib/server/api/middleware/rate-limiter.middleware'; +import { LoginRequestsService } from '$lib/server/api/services/loginrequest.service'; +import { SessionsService } from '$lib/server/api/services/sessions.service'; +import { UsersService } from '$lib/server/api/services/users.service'; +import { zValidator } from '@hono/zod-validator'; +import { setCookie } from 'hono/cookie'; +import { TimeSpan } from 'oslo'; +import { inject, injectable } from 'tsyringe'; @injectable() export class SignupController extends Controller { constructor( @inject(UsersService) private readonly usersService: UsersService, @inject(LoginRequestsService) private readonly loginRequestService: LoginRequestsService, - @inject(LuciaService) private luciaService: LuciaService, + @inject(SessionsService) private luciaService: SessionsService, ) { - super() + super(); } routes() { return this.controller.post('/', zValidator('json', signupUsernameEmailDto), limiter({ limit: 10, minutes: 60 }), async (c) => { - const { firstName, lastName, email, username, password, confirm_password } = await c.req.valid('json') - const existingUser = await this.usersService.findOneByUsername(username) + const { firstName, lastName, email, username, password, confirm_password } = await c.req.valid('json'); + const existingUser = await this.usersService.findOneByUsername(username); if (existingUser) { - return c.body('User already exists', 400) + return c.body('User already exists', 400); } - const user = await this.usersService.create({ firstName, lastName, email, username, password, confirm_password }) + const user = await this.usersService.create({ firstName, lastName, email, username, password, confirm_password }); if (!user) { - return c.body('Failed to create user', 500) + return c.body('Failed to create user', 500); } - const session = await this.loginRequestService.createUserSession(user.id, c.req, undefined) - const sessionCookie = this.luciaService.lucia.createSessionCookie(session.id) - console.log('set cookie', sessionCookie) + const session = await this.loginRequestService.createUserSession(user.id, c.req, undefined); + const sessionCookie = this.luciaService.lucia.createSessionCookie(session.id); + console.log('set cookie', sessionCookie); setCookie(c, sessionCookie.name, sessionCookie.value, { path: sessionCookie.attributes.path, maxAge: @@ -49,8 +49,8 @@ export class SignupController extends Controller { secure: sessionCookie.attributes.secure, httpOnly: sessionCookie.attributes.httpOnly, expires: sessionCookie.attributes.expires, - }) - return c.json({ message: 'ok' }) - }) + }); + return c.json({ message: 'ok' }); + }); } } diff --git a/src/lib/server/api/databases/migrate.ts b/src/lib/server/api/databases/migrate.ts deleted file mode 100644 index 3b8b1a7..0000000 --- a/src/lib/server/api/databases/migrate.ts +++ /dev/null @@ -1,30 +0,0 @@ -import 'dotenv/config' -import { drizzle } from 'drizzle-orm/postgres-js' -import { migrate } from 'drizzle-orm/postgres-js/migrator' -import postgres from 'postgres' -import config from '../../../../../drizzle.config' -import env from '../common/env' - -const connection = postgres({ - host: env.DATABASE_HOST || 'localhost', - port: env.DATABASE_PORT, - user: env.DATABASE_USER || 'root', - password: env.DATABASE_PASSWORD || '', - database: env.DATABASE_DB || 'boredgame', - ssl: false, // env.NODE_ENV === 'development' ? false : 'require', - max: 1, -}) -const db = drizzle(connection) - -try { - if (!config.out) { - console.error('No migrations folder specified in drizzle.config.ts') - process.exit() - } - await migrate(db, { migrationsFolder: config.out }) - console.log('Migrations complete') -} catch (e) { - console.error(e) -} - -process.exit() diff --git a/src/lib/server/api/databases/migrations/meta/_journal.json b/src/lib/server/api/databases/migrations/meta/_journal.json deleted file mode 100644 index 6022f03..0000000 --- a/src/lib/server/api/databases/migrations/meta/_journal.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "version": "7", - "dialect": "postgresql", - "entries": [ - { - "idx": 0, - "version": "7", - "when": 1725489682980, - "tag": "0000_volatile_warhawk", - "breakpoints": true - }, - { - "idx": 1, - "version": "7", - "when": 1726877846811, - "tag": "0001_pink_the_enforcers", - "breakpoints": true - } - ] -} \ No newline at end of file diff --git a/src/lib/server/api/databases/postgres/migrate.ts b/src/lib/server/api/databases/postgres/migrate.ts new file mode 100644 index 0000000..54276e0 --- /dev/null +++ b/src/lib/server/api/databases/postgres/migrate.ts @@ -0,0 +1,33 @@ +import 'dotenv/config'; +import { drizzle } from 'drizzle-orm/postgres-js'; +import { migrate } from 'drizzle-orm/postgres-js/migrator'; +import postgres from 'postgres'; +import config from '../../../../../../drizzle.config'; +import env from '../../common/env'; + +const connection = postgres({ + host: env.DATABASE_HOST || 'localhost', + port: env.DATABASE_PORT, + user: env.DATABASE_USER || 'root', + password: env.DATABASE_PASSWORD || '', + database: env.DATABASE_DB || 'boredgame', + ssl: false, // env.NODE_ENV === 'development' ? false : 'require', + max: 1, +}); +const db = drizzle(connection); + +try { + if (!config.out) { + console.error('No migrations folder specified in drizzle.config.ts'); + process.exit(); + } + if (!env.DB_MIGRATING) { + throw new Error('You must set DB_MIGRATING to "true" when running migrations.'); + } + await migrate(db, { migrationsFolder: config.out }); + console.log('Migrations complete'); +} catch (e) { + console.error(e); +} + +process.exit(); diff --git a/src/lib/server/api/databases/migrations/0000_volatile_warhawk.sql b/src/lib/server/api/databases/postgres/migrations/0000_volatile_warhawk.sql similarity index 100% rename from src/lib/server/api/databases/migrations/0000_volatile_warhawk.sql rename to src/lib/server/api/databases/postgres/migrations/0000_volatile_warhawk.sql diff --git a/src/lib/server/api/databases/migrations/0001_pink_the_enforcers.sql b/src/lib/server/api/databases/postgres/migrations/0001_pink_the_enforcers.sql similarity index 100% rename from src/lib/server/api/databases/migrations/0001_pink_the_enforcers.sql rename to src/lib/server/api/databases/postgres/migrations/0001_pink_the_enforcers.sql diff --git a/src/lib/server/api/databases/postgres/migrations/0002_mysterious_justice.sql b/src/lib/server/api/databases/postgres/migrations/0002_mysterious_justice.sql new file mode 100644 index 0000000..6c9bf0b --- /dev/null +++ b/src/lib/server/api/databases/postgres/migrations/0002_mysterious_justice.sql @@ -0,0 +1,7 @@ +ALTER TABLE "sessions" DROP CONSTRAINT "sessions_user_id_users_id_fk"; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "sessions" ADD CONSTRAINT "sessions_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; diff --git a/src/lib/server/api/databases/postgres/migrations/0003_far_siren.sql b/src/lib/server/api/databases/postgres/migrations/0003_far_siren.sql new file mode 100644 index 0000000..1f525a1 --- /dev/null +++ b/src/lib/server/api/databases/postgres/migrations/0003_far_siren.sql @@ -0,0 +1,2 @@ +ALTER TABLE "two_factor" DROP CONSTRAINT "two_factor_user_id_unique";--> statement-breakpoint +ALTER TABLE "two_factor" ADD CONSTRAINT "two_factor_userId_unique" UNIQUE("user_id"); \ No newline at end of file diff --git a/src/lib/server/api/databases/postgres/migrations/0004_lush_virginia_dare.sql b/src/lib/server/api/databases/postgres/migrations/0004_lush_virginia_dare.sql new file mode 100644 index 0000000..ae559c5 --- /dev/null +++ b/src/lib/server/api/databases/postgres/migrations/0004_lush_virginia_dare.sql @@ -0,0 +1,2 @@ +ALTER TABLE "two_factor" DROP CONSTRAINT "two_factor_userId_unique";--> statement-breakpoint +ALTER TABLE "two_factor" ADD CONSTRAINT "two_factor_user_id_unique" UNIQUE("user_id"); \ No newline at end of file diff --git a/src/lib/server/api/databases/migrations/meta/0000_snapshot.json b/src/lib/server/api/databases/postgres/migrations/meta/0000_snapshot.json similarity index 100% rename from src/lib/server/api/databases/migrations/meta/0000_snapshot.json rename to src/lib/server/api/databases/postgres/migrations/meta/0000_snapshot.json diff --git a/src/lib/server/api/databases/migrations/meta/0001_snapshot.json b/src/lib/server/api/databases/postgres/migrations/meta/0001_snapshot.json similarity index 100% rename from src/lib/server/api/databases/migrations/meta/0001_snapshot.json rename to src/lib/server/api/databases/postgres/migrations/meta/0001_snapshot.json diff --git a/src/lib/server/api/databases/postgres/migrations/meta/0002_snapshot.json b/src/lib/server/api/databases/postgres/migrations/meta/0002_snapshot.json new file mode 100644 index 0000000..2fc2b97 --- /dev/null +++ b/src/lib/server/api/databases/postgres/migrations/meta/0002_snapshot.json @@ -0,0 +1,1876 @@ +{ + "id": "40174c7c-e4ad-4356-9881-d1da97529e87", + "prevId": "e1230cae-67ce-4669-885a-3d3fe4462f9e", + "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": "cascade", + "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 + }, + "email_verified": { + "name": "email_verified", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "picture": { + "name": "picture", + "type": "text", + "primaryKey": false, + "notNull": 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/databases/postgres/migrations/meta/0003_snapshot.json b/src/lib/server/api/databases/postgres/migrations/meta/0003_snapshot.json new file mode 100644 index 0000000..8aebbbf --- /dev/null +++ b/src/lib/server/api/databases/postgres/migrations/meta/0003_snapshot.json @@ -0,0 +1,1957 @@ +{ + "id": "7a3a1569-bd52-484a-9acf-e538ac43c0ab", + "prevId": "40174c7c-e4ad-4356-9881-d1da97529e87", + "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" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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_userId_unique": { + "name": "two_factor_userId_unique", + "nullsNotDistinct": false, + "columns": [ + "user_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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 + }, + "email_verified": { + "name": "email_verified", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "picture": { + "name": "picture", + "type": "text", + "primaryKey": false, + "notNull": 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" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.external_id_type": { + "name": "external_id_type", + "schema": "public", + "values": [ + "game", + "category", + "mechanic", + "publisher", + "designer", + "artist" + ] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/src/lib/server/api/databases/postgres/migrations/meta/0004_snapshot.json b/src/lib/server/api/databases/postgres/migrations/meta/0004_snapshot.json new file mode 100644 index 0000000..687e4c8 --- /dev/null +++ b/src/lib/server/api/databases/postgres/migrations/meta/0004_snapshot.json @@ -0,0 +1,1957 @@ +{ + "id": "ad9fe944-b9f6-4de9-ac41-9f1765dee1bb", + "prevId": "7a3a1569-bd52-484a-9acf-e538ac43c0ab", + "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" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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 + }, + "email_verified": { + "name": "email_verified", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "picture": { + "name": "picture", + "type": "text", + "primaryKey": false, + "notNull": 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" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.external_id_type": { + "name": "external_id_type", + "schema": "public", + "values": [ + "game", + "category", + "mechanic", + "publisher", + "designer", + "artist" + ] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/src/lib/server/api/databases/postgres/migrations/meta/_journal.json b/src/lib/server/api/databases/postgres/migrations/meta/_journal.json new file mode 100644 index 0000000..f251ad7 --- /dev/null +++ b/src/lib/server/api/databases/postgres/migrations/meta/_journal.json @@ -0,0 +1,41 @@ +{ + "version": "7", + "dialect": "postgresql", + "entries": [ + { + "idx": 0, + "version": "7", + "when": 1725489682980, + "tag": "0000_volatile_warhawk", + "breakpoints": true + }, + { + "idx": 1, + "version": "7", + "when": 1726877846811, + "tag": "0001_pink_the_enforcers", + "breakpoints": true + }, + { + "idx": 2, + "version": "7", + "when": 1729273181237, + "tag": "0002_mysterious_justice", + "breakpoints": true + }, + { + "idx": 3, + "version": "7", + "when": 1730914649946, + "tag": "0003_far_siren", + "breakpoints": true + }, + { + "idx": 4, + "version": "7", + "when": 1730914773500, + "tag": "0004_lush_virginia_dare", + "breakpoints": true + } + ] +} \ No newline at end of file diff --git a/src/lib/server/api/databases/schemas/collections.schema.ts b/src/lib/server/api/databases/postgres/schemas/collections.schema.ts similarity index 82% rename from src/lib/server/api/databases/schemas/collections.schema.ts rename to src/lib/server/api/databases/postgres/schemas/collections.schema.ts index ecb84c8..dd2958b 100644 --- a/src/lib/server/api/databases/schemas/collections.schema.ts +++ b/src/lib/server/api/databases/postgres/schemas/collections.schema.ts @@ -1,6 +1,6 @@ -import { collections } from '$lib/server/api/databases/tables' -import { createInsertSchema, createSelectSchema } from 'drizzle-zod' -import type { z } from 'zod' +import { collections } from '$lib/server/api/databases/postgres/tables'; +import { createInsertSchema, createSelectSchema } from 'drizzle-zod'; +import type { z } from 'zod'; export const InsertCollectionSchema = createInsertSchema(collections, { name: (schema) => @@ -10,15 +10,15 @@ export const InsertCollectionSchema = createInsertSchema(collections, { cuid: true, createdAt: true, updatedAt: true, -}) +}); -export type InsertCollectionSchema = z.infer +export type InsertCollectionSchema = z.infer; export const SelectCollectionSchema = createSelectSchema(collections).omit({ id: true, user_id: true, createdAt: true, updatedAt: true, -}) +}); -export type SelectUserSchema = z.infer +export type SelectUserSchema = z.infer; diff --git a/src/lib/server/api/databases/schemas/users.schemas.ts b/src/lib/server/api/databases/postgres/schemas/users.schemas.ts similarity index 71% rename from src/lib/server/api/databases/schemas/users.schemas.ts rename to src/lib/server/api/databases/postgres/schemas/users.schemas.ts index 7d01d3e..e309211 100644 --- a/src/lib/server/api/databases/schemas/users.schemas.ts +++ b/src/lib/server/api/databases/postgres/schemas/users.schemas.ts @@ -1,6 +1,6 @@ -import { usersTable } from '$lib/server/api/databases/tables' -import { createInsertSchema, createSelectSchema } from 'drizzle-zod' -import type { z } from 'zod' +import { usersTable } from '$lib/server/api/databases/postgres/tables'; +import { createInsertSchema, createSelectSchema } from 'drizzle-zod'; +import type { z } from 'zod'; export const InsertUserSchema = createInsertSchema(usersTable, { email: (schema) => schema.email.max(64).email().optional(), @@ -15,10 +15,10 @@ export const InsertUserSchema = createInsertSchema(usersTable, { cuid: true, createdAt: true, updatedAt: true, -}) +}); -export type InsertUserSchema = z.infer +export type InsertUserSchema = z.infer; -export const SelectUserSchema = createSelectSchema(usersTable) +export const SelectUserSchema = createSelectSchema(usersTable); -export type SelectUserSchema = z.infer +export type SelectUserSchema = z.infer; diff --git a/src/lib/server/api/databases/seed.ts b/src/lib/server/api/databases/postgres/seed.ts similarity index 64% rename from src/lib/server/api/databases/seed.ts rename to src/lib/server/api/databases/postgres/seed.ts index 0ce35cf..b512e9e 100644 --- a/src/lib/server/api/databases/seed.ts +++ b/src/lib/server/api/databases/postgres/seed.ts @@ -1,19 +1,19 @@ -import 'reflect-metadata' -import { type Table, getTableName, sql } from 'drizzle-orm' -import type { NodePgDatabase } from 'drizzle-orm/node-postgres' -import env from '../common/env' -import { DrizzleService } from '../services/drizzle.service' -import * as seeds from './seeds' -import * as schema from './tables' +import 'reflect-metadata'; +import { type Table, getTableName, sql } from 'drizzle-orm'; +import type { NodePgDatabase } from 'drizzle-orm/node-postgres'; +import env from '../../common/env'; +import { DrizzleService } from '../../services/drizzle.service'; +import * as seeds from './seeds'; +import * as schema from './tables'; -const drizzleService = new DrizzleService() +const drizzleService = new DrizzleService(); if (!env.DB_SEEDING) { - throw new Error('You must set DB_SEEDING to "true" when running seeds') + throw new Error('You must set DB_SEEDING to "true" when running seeds'); } async function resetTable(db: NodePgDatabase, table: Table) { - return db.execute(sql.raw(`TRUNCATE TABLE ${getTableName(table)} RESTART IDENTITY CASCADE`)) + return db.execute(sql.raw(`TRUNCATE TABLE ${getTableName(table)} RESTART IDENTITY CASCADE`)); } for (const table of [ @@ -45,11 +45,11 @@ for (const table of [ schema.wishlistsTable, ]) { // await db.delete(table); // clear tables without truncating / resetting ids - await resetTable(drizzleService.db, table) + await resetTable(drizzleService.db, table); } -await seeds.roles(drizzleService.db) -await seeds.users(drizzleService.db) +await seeds.roles(drizzleService.db); +await seeds.users(drizzleService.db); -await drizzleService.dispose() -process.exit() +await drizzleService.dispose(); +process.exit(); diff --git a/src/lib/server/api/databases/seeds/data/roles.json b/src/lib/server/api/databases/postgres/seeds/data/roles.json similarity index 100% rename from src/lib/server/api/databases/seeds/data/roles.json rename to src/lib/server/api/databases/postgres/seeds/data/roles.json diff --git a/src/lib/server/api/databases/seeds/data/users.json b/src/lib/server/api/databases/postgres/seeds/data/users.json similarity index 100% rename from src/lib/server/api/databases/seeds/data/users.json rename to src/lib/server/api/databases/postgres/seeds/data/users.json diff --git a/src/lib/server/api/databases/seeds/index.ts b/src/lib/server/api/databases/postgres/seeds/index.ts similarity index 100% rename from src/lib/server/api/databases/seeds/index.ts rename to src/lib/server/api/databases/postgres/seeds/index.ts diff --git a/src/lib/server/api/databases/postgres/seeds/roles.ts b/src/lib/server/api/databases/postgres/seeds/roles.ts new file mode 100644 index 0000000..f49b708 --- /dev/null +++ b/src/lib/server/api/databases/postgres/seeds/roles.ts @@ -0,0 +1,11 @@ +import type { db } from '../../../packages/drizzle'; +import * as schema from '../tables'; +import roles from './data/roles.json'; + +export default async function seed(db: db) { + console.log('Creating rolesTable ...'); + for (const role of roles) { + await db.insert(schema.rolesTable).values(role).onConflictDoNothing(); + } + console.log('Roles created.'); +} diff --git a/src/lib/server/api/databases/seeds/users.ts b/src/lib/server/api/databases/postgres/seeds/users.ts similarity index 95% rename from src/lib/server/api/databases/seeds/users.ts rename to src/lib/server/api/databases/postgres/seeds/users.ts index b847cc2..2102cb9 100644 --- a/src/lib/server/api/databases/seeds/users.ts +++ b/src/lib/server/api/databases/postgres/seeds/users.ts @@ -1,6 +1,6 @@ import { eq } from 'drizzle-orm'; -import type { db } from '../../packages/drizzle'; -import { HashingService } from '../../services/hashing.service'; +import type { db } from '../../../packages/drizzle'; +import { HashingService } from '../../../services/hashing.service'; import * as schema from '../tables'; import users from './data/users.json'; diff --git a/src/lib/server/api/databases/tables/categories.table.ts b/src/lib/server/api/databases/postgres/tables/categories.table.ts similarity index 57% rename from src/lib/server/api/databases/tables/categories.table.ts rename to src/lib/server/api/databases/postgres/tables/categories.table.ts index 8f32634..747692d 100644 --- a/src/lib/server/api/databases/tables/categories.table.ts +++ b/src/lib/server/api/databases/postgres/tables/categories.table.ts @@ -1,23 +1,23 @@ -import { createId as cuid2 } from '@paralleldrive/cuid2' -import { type InferSelectModel, relations } from 'drizzle-orm' -import { pgTable, text, uuid } from 'drizzle-orm/pg-core' -import { timestamps } from '../../common/utils/table' -import { categoriesToExternalIdsTable } from './categoriesToExternalIds.table' -import { categories_to_games_table } from './categoriesToGames.table' +import { createId as cuid2 } from '@paralleldrive/cuid2'; +import { type InferSelectModel, relations } from 'drizzle-orm'; +import { pgTable, text, uuid } from 'drizzle-orm/pg-core'; +import { timestamps } from '../../../common/utils/table'; +import { categoriesToExternalIdsTable } from './categoriesToExternalIds.table'; +import { categories_to_games_table } from './categoriesToGames.table'; export const categoriesTable = pgTable('categories', { - id: uuid('id').primaryKey().defaultRandom(), - cuid: text('cuid') + id: uuid().primaryKey().defaultRandom(), + cuid: text() .unique() .$defaultFn(() => cuid2()), - name: text('name'), - slug: text('slug'), + name: text(), + slug: text(), ...timestamps, -}) +}); -export type Categories = InferSelectModel +export type Categories = InferSelectModel; export const categories_relations = relations(categoriesTable, ({ many }) => ({ categories_to_games: many(categories_to_games_table), categoriesToExternalIds: many(categoriesToExternalIdsTable), -})) +})); diff --git a/src/lib/server/api/databases/tables/categoriesToExternalIds.table.ts b/src/lib/server/api/databases/postgres/tables/categoriesToExternalIds.table.ts similarity index 73% rename from src/lib/server/api/databases/tables/categoriesToExternalIds.table.ts rename to src/lib/server/api/databases/postgres/tables/categoriesToExternalIds.table.ts index 6825bf4..2176ffd 100644 --- a/src/lib/server/api/databases/tables/categoriesToExternalIds.table.ts +++ b/src/lib/server/api/databases/postgres/tables/categoriesToExternalIds.table.ts @@ -1,15 +1,15 @@ -import { relations } from 'drizzle-orm' -import { pgTable, primaryKey, uuid } from 'drizzle-orm/pg-core' -import { categoriesTable } from './categories.table' -import { externalIdsTable } from './externalIds.table' +import { relations } from 'drizzle-orm'; +import { pgTable, primaryKey, uuid } from 'drizzle-orm/pg-core'; +import { categoriesTable } from './categories.table'; +import { externalIdsTable } from './externalIds.table'; export const categoriesToExternalIdsTable = pgTable( 'categories_to_external_ids', { - categoryId: uuid('category_id') + categoryId: uuid() .notNull() .references(() => categoriesTable.id, { onDelete: 'restrict', onUpdate: 'cascade' }), - externalId: uuid('external_id') + externalId: uuid() .notNull() .references(() => externalIdsTable.id, { onDelete: 'restrict', onUpdate: 'cascade' }), }, @@ -18,9 +18,9 @@ export const categoriesToExternalIdsTable = pgTable( categoriesToExternalIdsPkey: primaryKey({ columns: [table.categoryId, table.externalId], }), - } + }; }, -) +); export const categoriesToExternalIdsRelations = relations(categoriesToExternalIdsTable, ({ one }) => ({ category: one(categoriesTable, { @@ -31,4 +31,4 @@ export const categoriesToExternalIdsRelations = relations(categoriesToExternalId fields: [categoriesToExternalIdsTable.externalId], references: [externalIdsTable.id], }), -})) +})); diff --git a/src/lib/server/api/databases/tables/categoriesToGames.table.ts b/src/lib/server/api/databases/postgres/tables/categoriesToGames.table.ts similarity index 72% rename from src/lib/server/api/databases/tables/categoriesToGames.table.ts rename to src/lib/server/api/databases/postgres/tables/categoriesToGames.table.ts index 80bb6c5..55703ae 100644 --- a/src/lib/server/api/databases/tables/categoriesToGames.table.ts +++ b/src/lib/server/api/databases/postgres/tables/categoriesToGames.table.ts @@ -1,15 +1,15 @@ -import { relations } from 'drizzle-orm' -import { pgTable, primaryKey, uuid } from 'drizzle-orm/pg-core' -import { gamesTable } from '././games.table' -import { categoriesTable } from './categories.table' +import { relations } from 'drizzle-orm'; +import { pgTable, primaryKey, uuid } from 'drizzle-orm/pg-core'; +import { categoriesTable } from './categories.table'; +import { gamesTable } from './games.table'; export const categories_to_games_table = pgTable( 'categories_to_games', { - category_id: uuid('category_id') + category_id: uuid() .notNull() .references(() => categoriesTable.id, { onDelete: 'restrict', onUpdate: 'cascade' }), - game_id: uuid('game_id') + game_id: uuid() .notNull() .references(() => gamesTable.id, { onDelete: 'restrict', onUpdate: 'cascade' }), }, @@ -18,9 +18,9 @@ export const categories_to_games_table = pgTable( categoriesToGamesPkey: primaryKey({ columns: [table.category_id, table.game_id], }), - } + }; }, -) +); export const categories_to_games_relations = relations(categories_to_games_table, ({ one }) => ({ category: one(categoriesTable, { @@ -31,4 +31,4 @@ export const categories_to_games_relations = relations(categories_to_games_table fields: [categories_to_games_table.game_id], references: [gamesTable.id], }), -})) +})); diff --git a/src/lib/server/api/databases/tables/collectionItems.table.ts b/src/lib/server/api/databases/postgres/tables/collectionItems.table.ts similarity index 59% rename from src/lib/server/api/databases/tables/collectionItems.table.ts rename to src/lib/server/api/databases/postgres/tables/collectionItems.table.ts index 722a707..f7737c2 100644 --- a/src/lib/server/api/databases/tables/collectionItems.table.ts +++ b/src/lib/server/api/databases/postgres/tables/collectionItems.table.ts @@ -1,26 +1,26 @@ -import { createId as cuid2 } from '@paralleldrive/cuid2' -import { type InferSelectModel, relations } from 'drizzle-orm' -import { integer, pgTable, text, uuid } from 'drizzle-orm/pg-core' -import { timestamps } from '../../common/utils/table' -import { gamesTable } from '././games.table' -import { collections } from './collections.table' +import { createId as cuid2 } from '@paralleldrive/cuid2'; +import { type InferSelectModel, relations } from 'drizzle-orm'; +import { integer, pgTable, text, uuid } from 'drizzle-orm/pg-core'; +import { timestamps } from '../../../common/utils/table'; +import { collections } from './collections.table'; +import { gamesTable } from './games.table'; export const collection_items = pgTable('collection_items', { - id: uuid('id').primaryKey().defaultRandom(), - cuid: text('cuid') + id: uuid().primaryKey().defaultRandom(), + cuid: text() .unique() .$defaultFn(() => cuid2()), - collection_id: uuid('collection_id') + collection_id: uuid() .notNull() .references(() => collections.id, { onDelete: 'cascade' }), - game_id: uuid('game_id') + game_id: uuid() .notNull() .references(() => gamesTable.id, { onDelete: 'cascade' }), - times_played: integer('times_played').default(0), + times_played: integer().default(0), ...timestamps, -}) +}); -export type CollectionItemsTable = InferSelectModel +export type CollectionItemsTable = InferSelectModel; export const collection_item_relations = relations(collection_items, ({ one }) => ({ collection: one(collections, { @@ -31,4 +31,4 @@ export const collection_item_relations = relations(collection_items, ({ one }) = fields: [collection_items.game_id], references: [gamesTable.id], }), -})) +})); diff --git a/src/lib/server/api/databases/tables/collections.table.ts b/src/lib/server/api/databases/postgres/tables/collections.table.ts similarity index 81% rename from src/lib/server/api/databases/tables/collections.table.ts rename to src/lib/server/api/databases/postgres/tables/collections.table.ts index 7c29f91..fddeffd 100644 --- a/src/lib/server/api/databases/tables/collections.table.ts +++ b/src/lib/server/api/databases/postgres/tables/collections.table.ts @@ -2,19 +2,19 @@ import { createId as cuid2 } from '@paralleldrive/cuid2'; import { type InferSelectModel, relations } from 'drizzle-orm'; import { pgTable, text, uuid } from 'drizzle-orm/pg-core'; import { createSelectSchema } from 'drizzle-zod'; -import { timestamps } from '../../common/utils/table'; +import { timestamps } from '../../../common/utils/table'; import { collection_items } from './collectionItems.table'; import { usersTable } from './users.table'; export const collections = pgTable('collections', { - id: uuid('id').primaryKey().defaultRandom(), - cuid: text('cuid') + id: uuid().primaryKey().defaultRandom(), + cuid: text() .unique() .$defaultFn(() => cuid2()), - user_id: uuid('user_id') + user_id: uuid() .notNull() .references(() => usersTable.id, { onDelete: 'cascade' }), - name: text('name').notNull().default('My Collection'), + name: text().notNull().default('My Collection'), ...timestamps, }); diff --git a/src/lib/server/api/databases/postgres/tables/credentials.table.ts b/src/lib/server/api/databases/postgres/tables/credentials.table.ts new file mode 100644 index 0000000..e19ba80 --- /dev/null +++ b/src/lib/server/api/databases/postgres/tables/credentials.table.ts @@ -0,0 +1,23 @@ +import type { InferSelectModel } from 'drizzle-orm'; +import { pgTable, text, uuid } from 'drizzle-orm/pg-core'; +import { timestamps } from '../../../common/utils/table'; +import { usersTable } from './users.table'; + +export enum CredentialsType { + SECRET = 'secret', + PASSWORD = 'password', + TOTP = 'totp', + HOTP = 'hotp', +} + +export const credentialsTable = pgTable('credentials', { + id: uuid().primaryKey().defaultRandom(), + user_id: uuid() + .notNull() + .references(() => usersTable.id, { onDelete: 'cascade' }), + type: text().notNull().default(CredentialsType.PASSWORD), + secret_data: text().notNull(), + ...timestamps, +}); + +export type Credentials = InferSelectModel; diff --git a/src/lib/server/api/databases/tables/expansions.table.ts b/src/lib/server/api/databases/postgres/tables/expansions.table.ts similarity index 61% rename from src/lib/server/api/databases/tables/expansions.table.ts rename to src/lib/server/api/databases/postgres/tables/expansions.table.ts index fc9e634..6154187 100644 --- a/src/lib/server/api/databases/tables/expansions.table.ts +++ b/src/lib/server/api/databases/postgres/tables/expansions.table.ts @@ -1,24 +1,24 @@ -import { createId as cuid2 } from '@paralleldrive/cuid2' -import { type InferSelectModel, relations } from 'drizzle-orm' -import { pgTable, text, uuid } from 'drizzle-orm/pg-core' -import { timestamps } from '../../common/utils/table' -import { gamesTable } from '././games.table' +import { createId as cuid2 } from '@paralleldrive/cuid2'; +import { type InferSelectModel, relations } from 'drizzle-orm'; +import { pgTable, text, uuid } from 'drizzle-orm/pg-core'; +import { timestamps } from '../../../common/utils/table'; +import { gamesTable } from './games.table'; export const expansionsTable = pgTable('expansions', { - id: uuid('id').primaryKey().defaultRandom(), - cuid: text('cuid') + id: uuid().primaryKey().defaultRandom(), + cuid: text() .unique() .$defaultFn(() => cuid2()), - base_game_id: uuid('base_game_id') + base_game_id: uuid() .notNull() .references(() => gamesTable.id, { onDelete: 'restrict', onUpdate: 'cascade' }), - game_id: uuid('game_id') + game_id: uuid() .notNull() .references(() => gamesTable.id, { onDelete: 'restrict', onUpdate: 'cascade' }), ...timestamps, -}) +}); -export type Expansions = InferSelectModel +export type Expansions = InferSelectModel; export const expansion_relations = relations(expansionsTable, ({ one }) => ({ baseGame: one(gamesTable, { @@ -29,4 +29,4 @@ export const expansion_relations = relations(expansionsTable, ({ one }) => ({ fields: [expansionsTable.game_id], references: [gamesTable.id], }), -})) +})); diff --git a/src/lib/server/api/databases/postgres/tables/expansions.ts b/src/lib/server/api/databases/postgres/tables/expansions.ts new file mode 100644 index 0000000..4d7d800 --- /dev/null +++ b/src/lib/server/api/databases/postgres/tables/expansions.ts @@ -0,0 +1,32 @@ +import { createId as cuid2 } from '@paralleldrive/cuid2'; +import { type InferSelectModel, relations } from 'drizzle-orm'; +import { pgTable, text, uuid } from 'drizzle-orm/pg-core'; +import { timestamps } from '../../../common/utils/table'; +import { gamesTable } from './games.table'; + +export const expansions = pgTable('expansions', { + id: uuid().primaryKey().defaultRandom(), + cuid: text() + .unique() + .$defaultFn(() => cuid2()), + base_game_id: uuid() + .notNull() + .references(() => gamesTable.id, { onDelete: 'restrict', onUpdate: 'cascade' }), + game_id: uuid() + .notNull() + .references(() => gamesTable.id, { onDelete: 'restrict', onUpdate: 'cascade' }), + ...timestamps, +}); + +export type Expansions = InferSelectModel; + +export const expansion_relations = relations(expansions, ({ one }) => ({ + baseGame: one(gamesTable, { + fields: [expansions.base_game_id], + references: [gamesTable.id], + }), + game: one(gamesTable, { + fields: [expansions.game_id], + references: [gamesTable.id], + }), +})); diff --git a/src/lib/server/api/databases/postgres/tables/externalIds.table.ts b/src/lib/server/api/databases/postgres/tables/externalIds.table.ts new file mode 100644 index 0000000..6f4b0d4 --- /dev/null +++ b/src/lib/server/api/databases/postgres/tables/externalIds.table.ts @@ -0,0 +1,16 @@ +import { createId as cuid2 } from '@paralleldrive/cuid2'; +import type { InferSelectModel } from 'drizzle-orm'; +import { pgEnum, pgTable, text, uuid } from 'drizzle-orm/pg-core'; + +export const externalIdType = pgEnum('external_id_type', ['game', 'category', 'mechanic', 'publisher', 'designer', 'artist']); + +export const externalIdsTable = pgTable('external_ids', { + id: uuid().primaryKey().defaultRandom(), + cuid: text() + .unique() + .$defaultFn(() => cuid2()), + type: externalIdType(), + externalId: text().notNull(), +}); + +export type ExternalIds = InferSelectModel; diff --git a/src/lib/server/api/databases/postgres/tables/federatedIdentity.table.ts b/src/lib/server/api/databases/postgres/tables/federatedIdentity.table.ts new file mode 100644 index 0000000..dcc57ca --- /dev/null +++ b/src/lib/server/api/databases/postgres/tables/federatedIdentity.table.ts @@ -0,0 +1,17 @@ +import type { InferSelectModel } from 'drizzle-orm'; +import { pgTable, text, uuid } from 'drizzle-orm/pg-core'; +import { timestamps } from '../../../common/utils/table'; +import { usersTable } from './users.table'; + +export const federatedIdentityTable = pgTable('federated_identity', { + id: uuid().primaryKey().defaultRandom(), + user_id: uuid() + .notNull() + .references(() => usersTable.id, { onDelete: 'cascade' }), + identity_provider: text().notNull(), + federated_user_id: text().notNull(), + federated_username: text().notNull(), + ...timestamps, +}); + +export type FederatedIdentity = InferSelectModel; diff --git a/src/lib/server/api/databases/postgres/tables/games.table.ts b/src/lib/server/api/databases/postgres/tables/games.table.ts new file mode 100644 index 0000000..f6545f7 --- /dev/null +++ b/src/lib/server/api/databases/postgres/tables/games.table.ts @@ -0,0 +1,51 @@ +import { createId as cuid2 } from '@paralleldrive/cuid2'; +import { type InferSelectModel, relations, sql } from 'drizzle-orm'; +import { index, integer, pgTable, text, timestamp, uuid } from 'drizzle-orm/pg-core'; +import { timestamps } from '../../../common/utils/table'; +import { categories_to_games_table } from './categoriesToGames.table'; +import { gamesToExternalIdsTable } from './gamesToExternalIds.table'; +import { mechanics_to_games } from './mechanicsToGames.table'; +import { publishers_to_games } from './publishersToGames.table'; + +export const gamesTable = pgTable( + 'games', + { + id: uuid().primaryKey().defaultRandom(), + cuid: text() + .unique() + .$defaultFn(() => cuid2()), + name: text().notNull(), + slug: text().notNull(), + description: text(), + year_published: integer(), + min_players: integer(), + max_players: integer(), + playtime: integer(), + min_playtime: integer(), + max_playtime: integer(), + min_age: integer(), + image_url: text(), + thumb_url: text(), + url: text(), + last_sync_at: timestamp(), + ...timestamps, + }, + (table) => ({ + searchIndex: index('search_index').using( + 'gin', + sql`( + setweight(to_tsvector('english', ${table.name}), 'A') || + setweight(to_tsvector('english', ${table.slug}), 'B') + )`, + ), + }), +); + +export const gameRelations = relations(gamesTable, ({ many }) => ({ + categories_to_games: many(categories_to_games_table), + mechanics_to_games: many(mechanics_to_games), + publishers_to_games: many(publishers_to_games), + gamesToExternalIds: many(gamesToExternalIdsTable), +})); + +export type Games = InferSelectModel; diff --git a/src/lib/server/api/databases/tables/gamesToExternalIds.table.ts b/src/lib/server/api/databases/postgres/tables/gamesToExternalIds.table.ts similarity index 72% rename from src/lib/server/api/databases/tables/gamesToExternalIds.table.ts rename to src/lib/server/api/databases/postgres/tables/gamesToExternalIds.table.ts index 3d56ccc..ef58b7e 100644 --- a/src/lib/server/api/databases/tables/gamesToExternalIds.table.ts +++ b/src/lib/server/api/databases/postgres/tables/gamesToExternalIds.table.ts @@ -1,15 +1,15 @@ -import { pgTable, primaryKey, uuid } from 'drizzle-orm/pg-core' -import { gamesTable } from '././games.table' -import { externalIdsTable } from './externalIds.table' -import { relations } from 'drizzle-orm' +import { relations } from 'drizzle-orm'; +import { pgTable, primaryKey, uuid } from 'drizzle-orm/pg-core'; +import { externalIdsTable } from './externalIds.table'; +import { gamesTable } from './games.table'; export const gamesToExternalIdsTable = pgTable( 'games_to_external_ids', { - gameId: uuid('game_id') + gameId: uuid() .notNull() .references(() => gamesTable.id, { onDelete: 'restrict', onUpdate: 'cascade' }), - externalId: uuid('external_id') + externalId: uuid() .notNull() .references(() => externalIdsTable.id, { onDelete: 'restrict', onUpdate: 'cascade' }), }, @@ -18,9 +18,9 @@ export const gamesToExternalIdsTable = pgTable( gamesToExternalIdsPkey: primaryKey({ columns: [table.gameId, table.externalId], }), - } + }; }, -) +); export const gamesToExternalIdsRelations = relations(gamesToExternalIdsTable, ({ one }) => ({ game: one(gamesTable, { @@ -31,4 +31,4 @@ export const gamesToExternalIdsRelations = relations(gamesToExternalIdsTable, ({ fields: [gamesToExternalIdsTable.externalId], references: [externalIdsTable.id], }), -})) \ No newline at end of file +})); diff --git a/src/lib/server/api/databases/tables/index.ts b/src/lib/server/api/databases/postgres/tables/index.ts similarity index 100% rename from src/lib/server/api/databases/tables/index.ts rename to src/lib/server/api/databases/postgres/tables/index.ts diff --git a/src/lib/server/api/databases/postgres/tables/mechanics.table.ts b/src/lib/server/api/databases/postgres/tables/mechanics.table.ts new file mode 100644 index 0000000..4f9f4c2 --- /dev/null +++ b/src/lib/server/api/databases/postgres/tables/mechanics.table.ts @@ -0,0 +1,23 @@ +import { createId as cuid2 } from '@paralleldrive/cuid2'; +import { type InferSelectModel, relations } from 'drizzle-orm'; +import { pgTable, text, uuid } from 'drizzle-orm/pg-core'; +import { timestamps } from '../../../common/utils/table'; +import { mechanicsToExternalIdsTable } from './mechanicsToExternalIds.table'; +import { mechanics_to_games } from './mechanicsToGames.table'; + +export const mechanicsTable = pgTable('mechanics', { + id: uuid().primaryKey().defaultRandom(), + cuid: text() + .unique() + .$defaultFn(() => cuid2()), + name: text(), + slug: text(), + ...timestamps, +}); + +export type Mechanics = InferSelectModel; + +export const mechanics_relations = relations(mechanicsTable, ({ many }) => ({ + mechanics_to_games: many(mechanics_to_games), + mechanicsToExternalIds: many(mechanicsToExternalIdsTable), +})); diff --git a/src/lib/server/api/databases/postgres/tables/mechanics.ts b/src/lib/server/api/databases/postgres/tables/mechanics.ts new file mode 100644 index 0000000..42e5e98 --- /dev/null +++ b/src/lib/server/api/databases/postgres/tables/mechanics.ts @@ -0,0 +1,23 @@ +import { createId as cuid2 } from '@paralleldrive/cuid2'; +import { type InferSelectModel, relations } from 'drizzle-orm'; +import { pgTable, text, uuid } from 'drizzle-orm/pg-core'; +import { timestamps } from '../../../common/utils/table'; +import { mechanicsToExternalIdsTable } from './mechanicsToExternalIds.table'; +import { mechanics_to_games } from './mechanicsToGames.table'; + +export const mechanics = pgTable('mechanics', { + id: uuid().primaryKey().defaultRandom(), + cuid: text() + .unique() + .$defaultFn(() => cuid2()), + name: text(), + slug: text(), + ...timestamps, +}); + +export type Mechanics = InferSelectModel; + +export const mechanics_relations = relations(mechanics, ({ many }) => ({ + mechanics_to_games: many(mechanics_to_games), + mechanicsToExternalIds: many(mechanicsToExternalIdsTable), +})); diff --git a/src/lib/server/api/databases/tables/mechanicsToExternalIds.table.ts b/src/lib/server/api/databases/postgres/tables/mechanicsToExternalIds.table.ts similarity index 100% rename from src/lib/server/api/databases/tables/mechanicsToExternalIds.table.ts rename to src/lib/server/api/databases/postgres/tables/mechanicsToExternalIds.table.ts diff --git a/src/lib/server/api/databases/tables/mechanicsToGames.table.ts b/src/lib/server/api/databases/postgres/tables/mechanicsToGames.table.ts similarity index 72% rename from src/lib/server/api/databases/tables/mechanicsToGames.table.ts rename to src/lib/server/api/databases/postgres/tables/mechanicsToGames.table.ts index eaf9e5a..0ef62d9 100644 --- a/src/lib/server/api/databases/tables/mechanicsToGames.table.ts +++ b/src/lib/server/api/databases/postgres/tables/mechanicsToGames.table.ts @@ -1,15 +1,15 @@ -import { relations } from 'drizzle-orm' -import { pgTable, primaryKey, uuid } from 'drizzle-orm/pg-core' -import { gamesTable } from '././games.table' -import { mechanicsTable } from './mechanics.table' +import { relations } from 'drizzle-orm'; +import { pgTable, primaryKey, uuid } from 'drizzle-orm/pg-core'; +import { gamesTable } from './games.table'; +import { mechanicsTable } from './mechanics.table'; export const mechanics_to_games = pgTable( 'mechanics_to_games', { - mechanic_id: uuid('mechanic_id') + mechanic_id: uuid() .notNull() .references(() => mechanicsTable.id, { onDelete: 'restrict', onUpdate: 'cascade' }), - game_id: uuid('game_id') + game_id: uuid() .notNull() .references(() => gamesTable.id, { onDelete: 'restrict', onUpdate: 'cascade' }), }, @@ -18,9 +18,9 @@ export const mechanics_to_games = pgTable( mechanicsToGamesPkey: primaryKey({ columns: [table.mechanic_id, table.game_id], }), - } + }; }, -) +); export const mechanics_to_games_relations = relations(mechanics_to_games, ({ one }) => ({ mechanic: one(mechanicsTable, { @@ -31,4 +31,4 @@ export const mechanics_to_games_relations = relations(mechanics_to_games, ({ one fields: [mechanics_to_games.game_id], references: [gamesTable.id], }), -})) +})); diff --git a/src/lib/server/api/databases/tables/passwordResetTokens.table.ts b/src/lib/server/api/databases/postgres/tables/passwordResetTokens.table.ts similarity index 61% rename from src/lib/server/api/databases/tables/passwordResetTokens.table.ts rename to src/lib/server/api/databases/postgres/tables/passwordResetTokens.table.ts index df5444f..d8bb6e6 100644 --- a/src/lib/server/api/databases/tables/passwordResetTokens.table.ts +++ b/src/lib/server/api/databases/postgres/tables/passwordResetTokens.table.ts @@ -1,25 +1,25 @@ -import { createId as cuid2 } from '@paralleldrive/cuid2' -import { type InferSelectModel, relations } from 'drizzle-orm' -import { pgTable, text, timestamp, uuid } from 'drizzle-orm/pg-core' -import { timestamps } from '../../common/utils/table' -import { usersTable } from './users.table' +import { createId as cuid2 } from '@paralleldrive/cuid2'; +import { type InferSelectModel, relations } from 'drizzle-orm'; +import { pgTable, text, timestamp, uuid } from 'drizzle-orm/pg-core'; +import { timestamps } from '../../../common/utils/table'; +import { usersTable } from './users.table'; export const password_reset_tokens = pgTable('password_reset_tokens', { - id: text('id') + id: text() .primaryKey() .$defaultFn(() => cuid2()), - user_id: uuid('user_id') + user_id: uuid() .notNull() .references(() => usersTable.id, { onDelete: 'cascade' }), - expires_at: timestamp('expires_at'), + expires_at: timestamp(), ...timestamps, -}) +}); -export type PasswordResetTokensTable = InferSelectModel +export type PasswordResetTokensTable = InferSelectModel; export const password_reset_token_relations = relations(password_reset_tokens, ({ one }) => ({ user: one(usersTable, { fields: [password_reset_tokens.user_id], references: [usersTable.id], }), -})) +})); diff --git a/src/lib/server/api/databases/tables/publishers.table.ts b/src/lib/server/api/databases/postgres/tables/publishers.table.ts similarity index 50% rename from src/lib/server/api/databases/tables/publishers.table.ts rename to src/lib/server/api/databases/postgres/tables/publishers.table.ts index fd909b6..551d19d 100644 --- a/src/lib/server/api/databases/tables/publishers.table.ts +++ b/src/lib/server/api/databases/postgres/tables/publishers.table.ts @@ -1,23 +1,23 @@ -import { createId as cuid2 } from '@paralleldrive/cuid2' -import { type InferSelectModel, relations } from 'drizzle-orm' -import { pgTable, text, uuid } from 'drizzle-orm/pg-core' -import { timestamps } from '../../common/utils/table' -import { publishersToExternalIdsTable } from './publishersToExternalIds.table' -import { publishers_to_games } from './publishersToGames.table' +import { createId as cuid2 } from '@paralleldrive/cuid2'; +import { type InferSelectModel, relations } from 'drizzle-orm'; +import { pgTable, text, uuid } from 'drizzle-orm/pg-core'; +import { timestamps } from '../../../common/utils/table'; +import { publishersToExternalIdsTable } from './publishersToExternalIds.table'; +import { publishers_to_games } from './publishersToGames.table'; export const publishersTable = pgTable('publishers', { - id: uuid('id').primaryKey().defaultRandom(), - cuid: text('cuid') + id: uuid().primaryKey().defaultRandom(), + cuid: text() .unique() .$defaultFn(() => cuid2()), - name: text('name'), - slug: text('slug'), + name: text(), + slug: text(), ...timestamps, -}) +}); -export type Publishers = InferSelectModel +export type Publishers = InferSelectModel; export const publishers_relations = relations(publishersTable, ({ many }) => ({ publishersToGames: many(publishers_to_games), publishersToExternalIds: many(publishersToExternalIdsTable), -})) \ No newline at end of file +})); diff --git a/src/lib/server/api/databases/postgres/tables/publishers.ts b/src/lib/server/api/databases/postgres/tables/publishers.ts new file mode 100644 index 0000000..10fa6c0 --- /dev/null +++ b/src/lib/server/api/databases/postgres/tables/publishers.ts @@ -0,0 +1,23 @@ +import { createId as cuid2 } from '@paralleldrive/cuid2'; +import { type InferSelectModel, relations } from 'drizzle-orm'; +import { pgTable, text, uuid } from 'drizzle-orm/pg-core'; +import { timestamps } from '../../../common/utils/table'; +import { publishersToExternalIdsTable } from './publishersToExternalIds.table'; +import { publishers_to_games } from './publishersToGames.table'; + +export const publishers = pgTable('publishers', { + id: uuid().primaryKey().defaultRandom(), + cuid: text() + .unique() + .$defaultFn(() => cuid2()), + name: text(), + slug: text(), + ...timestamps, +}); + +export type Publishers = InferSelectModel; + +export const publishers_relations = relations(publishers, ({ many }) => ({ + publishers_to_games: many(publishers_to_games), + publishersToExternalIds: many(publishersToExternalIdsTable), +})); diff --git a/src/lib/server/api/databases/tables/publishersToExternalIds.table.ts b/src/lib/server/api/databases/postgres/tables/publishersToExternalIds.table.ts similarity index 73% rename from src/lib/server/api/databases/tables/publishersToExternalIds.table.ts rename to src/lib/server/api/databases/postgres/tables/publishersToExternalIds.table.ts index 2249d2f..ae10c01 100644 --- a/src/lib/server/api/databases/tables/publishersToExternalIds.table.ts +++ b/src/lib/server/api/databases/postgres/tables/publishersToExternalIds.table.ts @@ -1,15 +1,15 @@ -import { pgTable, primaryKey, uuid } from 'drizzle-orm/pg-core' -import { externalIdsTable } from './externalIds.table' -import { publishersTable } from './publishers.table' -import { relations } from 'drizzle-orm' +import { relations } from 'drizzle-orm'; +import { pgTable, primaryKey, uuid } from 'drizzle-orm/pg-core'; +import { externalIdsTable } from './externalIds.table'; +import { publishersTable } from './publishers.table'; export const publishersToExternalIdsTable = pgTable( 'publishers_to_external_ids', { - publisherId: uuid('publisher_id') + publisherId: uuid() .notNull() .references(() => publishersTable.id, { onDelete: 'restrict', onUpdate: 'cascade' }), - externalId: uuid('external_id') + externalId: uuid() .notNull() .references(() => externalIdsTable.id, { onDelete: 'restrict', onUpdate: 'cascade' }), }, @@ -18,9 +18,9 @@ export const publishersToExternalIdsTable = pgTable( publishersToExternalIdsPkey: primaryKey({ columns: [table.publisherId, table.externalId], }), - } + }; }, -) +); export const publishersToExternalIdsRelations = relations(publishersToExternalIdsTable, ({ one }) => ({ publisher: one(publishersTable, { @@ -31,4 +31,4 @@ export const publishersToExternalIdsRelations = relations(publishersToExternalId fields: [publishersToExternalIdsTable.externalId], references: [externalIdsTable.id], }), -})) +})); diff --git a/src/lib/server/api/databases/tables/publishersToGames.table.ts b/src/lib/server/api/databases/postgres/tables/publishersToGames.table.ts similarity index 72% rename from src/lib/server/api/databases/tables/publishersToGames.table.ts rename to src/lib/server/api/databases/postgres/tables/publishersToGames.table.ts index 3d772a2..b664429 100644 --- a/src/lib/server/api/databases/tables/publishersToGames.table.ts +++ b/src/lib/server/api/databases/postgres/tables/publishersToGames.table.ts @@ -1,15 +1,15 @@ -import { relations } from 'drizzle-orm' -import { pgTable, primaryKey, uuid } from 'drizzle-orm/pg-core' -import { gamesTable } from '././games.table' -import { publishersTable } from './publishers.table' +import { relations } from 'drizzle-orm'; +import { pgTable, primaryKey, uuid } from 'drizzle-orm/pg-core'; +import { gamesTable } from './games.table'; +import { publishersTable } from './publishers.table'; export const publishers_to_games = pgTable( 'publishers_to_games', { - publisher_id: uuid('publisher_id') + publisher_id: uuid() .notNull() .references(() => publishersTable.id, { onDelete: 'restrict', onUpdate: 'cascade' }), - game_id: uuid('game_id') + game_id: uuid() .notNull() .references(() => gamesTable.id, { onDelete: 'restrict', onUpdate: 'cascade' }), }, @@ -18,9 +18,9 @@ export const publishers_to_games = pgTable( publishersToGamesPkey: primaryKey({ columns: [table.publisher_id, table.game_id], }), - } + }; }, -) +); export const publishers_to_games_relations = relations(publishers_to_games, ({ one }) => ({ publisher: one(publishersTable, { @@ -31,4 +31,4 @@ export const publishers_to_games_relations = relations(publishers_to_games, ({ o fields: [publishers_to_games.game_id], references: [gamesTable.id], }), -})) +})); diff --git a/src/lib/server/api/databases/postgres/tables/recovery-codes.table.ts b/src/lib/server/api/databases/postgres/tables/recovery-codes.table.ts new file mode 100644 index 0000000..61d5bcd --- /dev/null +++ b/src/lib/server/api/databases/postgres/tables/recovery-codes.table.ts @@ -0,0 +1,16 @@ +import type { InferSelectModel } from 'drizzle-orm'; +import { boolean, pgTable, text, uuid } from 'drizzle-orm/pg-core'; +import { timestamps } from '../../../common/utils/table'; +import { usersTable } from './users.table'; + +export const recoveryCodesTable = pgTable('recovery_codes', { + id: uuid().primaryKey().defaultRandom(), + userId: uuid() + .notNull() + .references(() => usersTable.id), + code: text().notNull(), + used: boolean().default(false), + ...timestamps, +}); + +export type RecoveryCodesTable = InferSelectModel; diff --git a/src/lib/server/api/databases/postgres/tables/roles.table.ts b/src/lib/server/api/databases/postgres/tables/roles.table.ts new file mode 100644 index 0000000..a4b864c --- /dev/null +++ b/src/lib/server/api/databases/postgres/tables/roles.table.ts @@ -0,0 +1,28 @@ +import { createId as cuid2 } from '@paralleldrive/cuid2'; +import { type InferSelectModel, relations } from 'drizzle-orm'; +import { pgTable, text, uuid } from 'drizzle-orm/pg-core'; +import { timestamps } from '../../../common/utils/table'; +import { user_roles } from './userRoles.table'; + +export enum RoleName { + ADMIN = 'admin', + EDITOR = 'editor', + MODERATOR = 'moderator', + USER = 'user', +} + +export const rolesTable = pgTable('roles', { + id: uuid().primaryKey().defaultRandom(), + cuid: text() + .unique() + .$defaultFn(() => cuid2()) + .notNull(), + name: text().unique().notNull(), + ...timestamps, +}); + +export type Roles = InferSelectModel; + +export const role_relations = relations(rolesTable, ({ many }) => ({ + user_roles: many(user_roles), +})); diff --git a/src/lib/server/api/databases/tables/sessions.table.ts b/src/lib/server/api/databases/postgres/tables/sessions.table.ts similarity index 53% rename from src/lib/server/api/databases/tables/sessions.table.ts rename to src/lib/server/api/databases/postgres/tables/sessions.table.ts index 2985393..33c4830 100644 --- a/src/lib/server/api/databases/tables/sessions.table.ts +++ b/src/lib/server/api/databases/postgres/tables/sessions.table.ts @@ -1,27 +1,28 @@ +import { type InferSelectModel, relations } from 'drizzle-orm'; import { boolean, pgTable, text, timestamp, uuid } from 'drizzle-orm/pg-core'; -import { relations, type InferSelectModel } from 'drizzle-orm'; +import { cuid2 } from '../../../common/utils/table'; import { usersTable } from './users.table'; export const sessionsTable = pgTable('sessions', { - id: text('id').primaryKey(), - userId: uuid('user_id') + id: cuid2().primaryKey(), + userId: uuid() .notNull() - .references(() => usersTable.id), - expiresAt: timestamp('expires_at', { + .references(() => usersTable.id, { onDelete: 'cascade' }), + expiresAt: timestamp({ withTimezone: true, mode: 'date', }).notNull(), - ipCountry: text('ip_country'), - ipAddress: text('ip_address'), - twoFactorAuthEnabled: boolean('two_factor_auth_enabled').default(false), - isTwoFactorAuthenticated: boolean('is_two_factor_authenticated').default(false), + ipCountry: text(), + ipAddress: text(), + twoFactorAuthEnabled: boolean().default(false), + isTwoFactorAuthenticated: boolean().default(false), }); export const sessionsRelations = relations(sessionsTable, ({ one }) => ({ user: one(usersTable, { fields: [sessionsTable.userId], references: [usersTable.id], - }) + }), })); export type Sessions = InferSelectModel; diff --git a/src/lib/server/api/databases/postgres/tables/two-factor.table.ts b/src/lib/server/api/databases/postgres/tables/two-factor.table.ts new file mode 100644 index 0000000..a46026d --- /dev/null +++ b/src/lib/server/api/databases/postgres/tables/two-factor.table.ts @@ -0,0 +1,32 @@ +import { createId as cuid2 } from '@paralleldrive/cuid2'; +import { type InferSelectModel, relations } from 'drizzle-orm'; +import { boolean, pgTable, text, timestamp, uuid } from 'drizzle-orm/pg-core'; +import { timestamps } from '../../../common/utils/table'; +import { usersTable } from './users.table'; + +export const twoFactorTable = pgTable('two_factor', { + id: uuid().primaryKey().defaultRandom(), + cuid: text() + .unique() + .$defaultFn(() => cuid2()), + secret: text().notNull(), + enabled: boolean().notNull().default(false), + initiatedTime: timestamp({ + mode: 'date', + withTimezone: true, + }), + userId: uuid() + .notNull() + .references(() => usersTable.id) + .unique('two_factor_user_id_unique'), + ...timestamps, +}); + +export const emailVerificationsRelations = relations(twoFactorTable, ({ one }) => ({ + user: one(usersTable, { + fields: [twoFactorTable.userId], + references: [usersTable.id], + }), +})); + +export type TwoFactor = InferSelectModel; diff --git a/src/lib/server/api/databases/tables/userRoles.table.ts b/src/lib/server/api/databases/postgres/tables/userRoles.table.ts similarity index 59% rename from src/lib/server/api/databases/tables/userRoles.table.ts rename to src/lib/server/api/databases/postgres/tables/userRoles.table.ts index e55d121..736eccd 100644 --- a/src/lib/server/api/databases/tables/userRoles.table.ts +++ b/src/lib/server/api/databases/postgres/tables/userRoles.table.ts @@ -1,24 +1,24 @@ -import { createId as cuid2 } from '@paralleldrive/cuid2' -import { type InferSelectModel, relations } from 'drizzle-orm' -import { boolean, pgTable, text, uuid } from 'drizzle-orm/pg-core' -import { timestamps } from '../../common/utils/table' -import { rolesTable } from './roles.table' -import { usersTable } from './users.table' +import { createId as cuid2 } from '@paralleldrive/cuid2'; +import { type InferSelectModel, relations } from 'drizzle-orm'; +import { boolean, pgTable, text, uuid } from 'drizzle-orm/pg-core'; +import { timestamps } from '../../../common/utils/table'; +import { rolesTable } from './roles.table'; +import { usersTable } from './users.table'; export const user_roles = pgTable('user_roles', { - id: uuid('id').primaryKey().defaultRandom(), - cuid: text('cuid') + id: uuid().primaryKey().defaultRandom(), + cuid: text() .unique() .$defaultFn(() => cuid2()), - user_id: uuid('user_id') + user_id: uuid() .notNull() .references(() => usersTable.id, { onDelete: 'cascade' }), - role_id: uuid('role_id') + role_id: uuid() .notNull() .references(() => rolesTable.id, { onDelete: 'cascade' }), - primary: boolean('primary').default(false), + primary: boolean().default(false), ...timestamps, -}) +}); export const user_role_relations = relations(user_roles, ({ one }) => ({ role: one(rolesTable, { @@ -29,6 +29,6 @@ export const user_role_relations = relations(user_roles, ({ one }) => ({ fields: [user_roles.user_id], references: [usersTable.id], }), -})) +})); -export type UserRolesTable = InferSelectModel +export type UserRolesTable = InferSelectModel; diff --git a/src/lib/server/api/databases/tables/users.table.ts b/src/lib/server/api/databases/postgres/tables/users.table.ts similarity index 53% rename from src/lib/server/api/databases/tables/users.table.ts rename to src/lib/server/api/databases/postgres/tables/users.table.ts index 6ae09df..5d7748f 100644 --- a/src/lib/server/api/databases/tables/users.table.ts +++ b/src/lib/server/api/databases/postgres/tables/users.table.ts @@ -2,24 +2,24 @@ import { createId as cuid2 } from '@paralleldrive/cuid2'; import { type InferSelectModel, relations } from 'drizzle-orm'; import { boolean, pgTable, text, uuid } from 'drizzle-orm/pg-core'; import { createSelectSchema } from 'drizzle-zod'; -import { timestamps } from '../../common/utils/table'; +import { timestamps } from '../../../common/utils/table'; import { user_roles } from './userRoles.table'; export const usersTable = pgTable('users', { - id: uuid('id').primaryKey().defaultRandom(), - cuid: text('cuid') + id: uuid().primaryKey().defaultRandom(), + cuid: text() .unique() .$defaultFn(() => cuid2()), - username: text('username').unique(), - email: text('email').unique(), - first_name: text('first_name'), - last_name: text('last_name'), - verified: boolean('verified').default(false), - receive_email: boolean('receive_email').default(false), - email_verified: boolean('email_verified').default(false), - picture: text('picture'), - mfa_enabled: boolean('mfa_enabled').notNull().default(false), - theme: text('theme').default('system'), + username: text().unique(), + email: text().unique(), + first_name: text(), + last_name: text(), + verified: boolean().default(false), + receive_email: boolean().default(false), + email_verified: boolean().default(false), + picture: text(), + mfa_enabled: boolean().notNull().default(false), + theme: text().default('system'), ...timestamps, }); diff --git a/src/lib/server/api/databases/tables/wishlistItems.table.ts b/src/lib/server/api/databases/postgres/tables/wishlistItems.table.ts similarity index 57% rename from src/lib/server/api/databases/tables/wishlistItems.table.ts rename to src/lib/server/api/databases/postgres/tables/wishlistItems.table.ts index 358395d..2ea76e1 100644 --- a/src/lib/server/api/databases/tables/wishlistItems.table.ts +++ b/src/lib/server/api/databases/postgres/tables/wishlistItems.table.ts @@ -1,25 +1,25 @@ -import { createId as cuid2 } from '@paralleldrive/cuid2' -import { type InferSelectModel, relations } from 'drizzle-orm' -import { pgTable, text, uuid } from 'drizzle-orm/pg-core' -import { timestamps } from '../../common/utils/table' -import { gamesTable } from '././games.table' -import { wishlistsTable } from './wishlists.table' +import { createId as cuid2 } from '@paralleldrive/cuid2'; +import { type InferSelectModel, relations } from 'drizzle-orm'; +import { pgTable, text, uuid } from 'drizzle-orm/pg-core'; +import { timestamps } from '../../../common/utils/table'; +import { gamesTable } from './games.table'; +import { wishlistsTable } from './wishlists.table'; export const wishlist_items = pgTable('wishlist_items', { - id: uuid('id').primaryKey().defaultRandom(), - cuid: text('cuid') + id: uuid().primaryKey().defaultRandom(), + cuid: text() .unique() .$defaultFn(() => cuid2()), - wishlist_id: uuid('wishlist_id') + wishlist_id: uuid() .notNull() .references(() => wishlistsTable.id, { onDelete: 'cascade' }), - game_id: uuid('game_id') + game_id: uuid() .notNull() .references(() => gamesTable.id, { onDelete: 'cascade' }), ...timestamps, -}) +}); -export type WishlistItemsTable = InferSelectModel +export type WishlistItemsTable = InferSelectModel; export const wishlist_item_relations = relations(wishlist_items, ({ one }) => ({ wishlist: one(wishlistsTable, { @@ -30,4 +30,4 @@ export const wishlist_item_relations = relations(wishlist_items, ({ one }) => ({ fields: [wishlist_items.game_id], references: [gamesTable.id], }), -})) +})); diff --git a/src/lib/server/api/databases/postgres/tables/wishlists.table.ts b/src/lib/server/api/databases/postgres/tables/wishlists.table.ts new file mode 100644 index 0000000..71b984c --- /dev/null +++ b/src/lib/server/api/databases/postgres/tables/wishlists.table.ts @@ -0,0 +1,26 @@ +import { createId as cuid2 } from '@paralleldrive/cuid2'; +import { type InferSelectModel, relations } from 'drizzle-orm'; +import { pgTable, text, uuid } from 'drizzle-orm/pg-core'; +import { timestamps } from '../../../common/utils/table'; +import { usersTable } from './users.table'; + +export const wishlistsTable = pgTable('wishlists', { + id: uuid().primaryKey().defaultRandom(), + cuid: text() + .unique() + .$defaultFn(() => cuid2()), + user_id: uuid() + .notNull() + .references(() => usersTable.id, { onDelete: 'cascade' }), + name: text().notNull().default('My Wishlist'), + ...timestamps, +}); + +export type Wishlists = InferSelectModel; + +export const wishlists_relations = relations(wishlistsTable, ({ one }) => ({ + user: one(usersTable, { + fields: [wishlistsTable.user_id], + references: [usersTable.id], + }), +})); diff --git a/src/lib/server/api/databases/seeds/roles.ts b/src/lib/server/api/databases/seeds/roles.ts deleted file mode 100644 index 1d5b0e7..0000000 --- a/src/lib/server/api/databases/seeds/roles.ts +++ /dev/null @@ -1,11 +0,0 @@ -import type { db } from '../../packages/drizzle' -import * as schema from '../tables' -import roles from './data/roles.json' - -export default async function seed(db: db) { - console.log('Creating rolesTable ...') - for (const role of roles) { - await db.insert(schema.rolesTable).values(role).onConflictDoNothing() - } - console.log('Roles created.') -} diff --git a/src/lib/server/api/databases/tables/credentials.table.ts b/src/lib/server/api/databases/tables/credentials.table.ts deleted file mode 100644 index 1a15b7d..0000000 --- a/src/lib/server/api/databases/tables/credentials.table.ts +++ /dev/null @@ -1,23 +0,0 @@ -import type { InferSelectModel } from 'drizzle-orm' -import { pgTable, text, uuid } from 'drizzle-orm/pg-core' -import { timestamps } from '../../common/utils/table' -import { usersTable } from './users.table' - -export enum CredentialsType { - SECRET = 'secret', - PASSWORD = 'password', - TOTP = 'totp', - HOTP = 'hotp', -} - -export const credentialsTable = pgTable('credentials', { - id: uuid('id').primaryKey().defaultRandom(), - user_id: uuid('user_id') - .notNull() - .references(() => usersTable.id, { onDelete: 'cascade' }), - type: text('type').notNull().default(CredentialsType.PASSWORD), - secret_data: text('secret_data').notNull(), - ...timestamps, -}) - -export type Credentials = InferSelectModel diff --git a/src/lib/server/api/databases/tables/expansions.ts b/src/lib/server/api/databases/tables/expansions.ts deleted file mode 100644 index a5ca59e..0000000 --- a/src/lib/server/api/databases/tables/expansions.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { createId as cuid2 } from '@paralleldrive/cuid2' -import { type InferSelectModel, relations } from 'drizzle-orm' -import { pgTable, text, uuid } from 'drizzle-orm/pg-core' -import { timestamps } from '../../common/utils/table.utils' -import { games } from './games' - -export const expansions = pgTable('expansions', { - id: uuid('id').primaryKey().defaultRandom(), - cuid: text('cuid') - .unique() - .$defaultFn(() => cuid2()), - base_game_id: uuid('base_game_id') - .notNull() - .references(() => games.id, { onDelete: 'restrict', onUpdate: 'cascade' }), - game_id: uuid('game_id') - .notNull() - .references(() => games.id, { onDelete: 'restrict', onUpdate: 'cascade' }), - ...timestamps, -}) - -export type Expansions = InferSelectModel - -export const expansion_relations = relations(expansions, ({ one }) => ({ - baseGame: one(games, { - fields: [expansions.base_game_id], - references: [games.id], - }), - game: one(games, { - fields: [expansions.game_id], - references: [games.id], - }), -})) diff --git a/src/lib/server/api/databases/tables/externalIds.table.ts b/src/lib/server/api/databases/tables/externalIds.table.ts deleted file mode 100644 index 0df8f31..0000000 --- a/src/lib/server/api/databases/tables/externalIds.table.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { createId as cuid2 } from '@paralleldrive/cuid2' -import type { InferSelectModel } from 'drizzle-orm' -import { pgEnum, pgTable, text, uuid } from 'drizzle-orm/pg-core' - -export const externalIdType = pgEnum('external_id_type', ['game', 'category', 'mechanic', 'publisher', 'designer', 'artist']) - -export const externalIdsTable = pgTable('external_ids', { - id: uuid('id').primaryKey().defaultRandom(), - cuid: text('cuid') - .unique() - .$defaultFn(() => cuid2()), - type: externalIdType('type'), - externalId: text('external_id').notNull(), -}) - -export type ExternalIds = InferSelectModel diff --git a/src/lib/server/api/databases/tables/federatedIdentity.table.ts b/src/lib/server/api/databases/tables/federatedIdentity.table.ts deleted file mode 100644 index 35b409b..0000000 --- a/src/lib/server/api/databases/tables/federatedIdentity.table.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { type InferSelectModel } from 'drizzle-orm' -import { pgTable, text, uuid } from 'drizzle-orm/pg-core' -import { timestamps } from '../../common/utils/table' -import { usersTable } from './users.table' - -export const federatedIdentityTable = pgTable('federated_identity', { - id: uuid('id').primaryKey().defaultRandom(), - user_id: uuid('user_id') - .notNull() - .references(() => usersTable.id, { onDelete: 'cascade' }), - identity_provider: text('identity_provider').notNull(), - federated_user_id: text('federated_user_id').notNull(), - federated_username: text('federated_username').notNull(), - ...timestamps, -}) - -export type FederatedIdentity = InferSelectModel diff --git a/src/lib/server/api/databases/tables/games.table.ts b/src/lib/server/api/databases/tables/games.table.ts deleted file mode 100644 index c2eb49e..0000000 --- a/src/lib/server/api/databases/tables/games.table.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { createId as cuid2 } from '@paralleldrive/cuid2' -import { type InferSelectModel, relations, sql } from 'drizzle-orm' -import { index, integer, pgTable, text, timestamp, uuid } from 'drizzle-orm/pg-core' -import { timestamps } from '../../common/utils/table' -import { categories_to_games_table } from './categoriesToGames.table' -import { gamesToExternalIdsTable } from './gamesToExternalIds.table' -import { mechanics_to_games } from './mechanicsToGames.table' -import { publishers_to_games } from './publishersToGames.table' - -export const gamesTable = pgTable( - 'games', - { - id: uuid('id').primaryKey().defaultRandom(), - cuid: text('cuid') - .unique() - .$defaultFn(() => cuid2()), - name: text('name').notNull(), - slug: text('slug').notNull(), - description: text('description'), - year_published: integer('year_published'), - min_players: integer('min_players'), - max_players: integer('max_players'), - playtime: integer('playtime'), - min_playtime: integer('min_playtime'), - max_playtime: integer('max_playtime'), - min_age: integer('min_age'), - image_url: text('image_url'), - thumb_url: text('thumb_url'), - url: text('url'), - last_sync_at: timestamp('last_sync_at'), - ...timestamps, - }, - (table) => ({ - searchIndex: index('search_index').using( - 'gin', - sql`( - setweight(to_tsvector('english', ${table.name}), 'A') || - setweight(to_tsvector('english', ${table.slug}), 'B') - )`, - ), - }), -) - -export const gameRelations = relations(gamesTable, ({ many }) => ({ - categories_to_games: many(categories_to_games_table), - mechanics_to_games: many(mechanics_to_games), - publishers_to_games: many(publishers_to_games), - gamesToExternalIds: many(gamesToExternalIdsTable), -})) - -export type Games = InferSelectModel diff --git a/src/lib/server/api/databases/tables/mechanics.table.ts b/src/lib/server/api/databases/tables/mechanics.table.ts deleted file mode 100644 index fab6757..0000000 --- a/src/lib/server/api/databases/tables/mechanics.table.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { createId as cuid2 } from '@paralleldrive/cuid2' -import { type InferSelectModel, relations } from 'drizzle-orm' -import { pgTable, text, uuid } from 'drizzle-orm/pg-core' -import { timestamps } from '../../common/utils/table' -import { mechanicsToExternalIdsTable } from './mechanicsToExternalIds.table' -import { mechanics_to_games } from './mechanicsToGames.table' - -export const mechanicsTable = pgTable('mechanics', { - id: uuid('id').primaryKey().defaultRandom(), - cuid: text('cuid') - .unique() - .$defaultFn(() => cuid2()), - name: text('name'), - slug: text('slug'), - ...timestamps, -}) - -export type Mechanics = InferSelectModel - -export const mechanics_relations = relations(mechanicsTable, ({ many }) => ({ - mechanics_to_games: many(mechanics_to_games), - mechanicsToExternalIds: many(mechanicsToExternalIdsTable), -})) diff --git a/src/lib/server/api/databases/tables/mechanics.ts b/src/lib/server/api/databases/tables/mechanics.ts deleted file mode 100644 index f5d4ced..0000000 --- a/src/lib/server/api/databases/tables/mechanics.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { timestamps } from '../../common/utils/table.utils' -import { createId as cuid2 } from '@paralleldrive/cuid2' -import { type InferSelectModel, relations } from 'drizzle-orm' -import { pgTable, text, uuid } from 'drizzle-orm/pg-core' -import { mechanicsToExternalIds } from './mechanicsToExternalIds' -import { mechanics_to_games } from './mechanicsToGames' - -export const mechanics = pgTable('mechanics', { - id: uuid('id').primaryKey().defaultRandom(), - cuid: text('cuid') - .unique() - .$defaultFn(() => cuid2()), - name: text('name'), - slug: text('slug'), - ...timestamps, -}) - -export type Mechanics = InferSelectModel - -export const mechanics_relations = relations(mechanics, ({ many }) => ({ - mechanics_to_games: many(mechanics_to_games), - mechanicsToExternalIds: many(mechanicsToExternalIds), -})) diff --git a/src/lib/server/api/databases/tables/publishers.ts b/src/lib/server/api/databases/tables/publishers.ts deleted file mode 100644 index 3000625..0000000 --- a/src/lib/server/api/databases/tables/publishers.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { timestamps } from '../../common/utils/table.utils' -import { createId as cuid2 } from '@paralleldrive/cuid2' -import { type InferSelectModel, relations } from 'drizzle-orm' -import { pgTable, text, uuid } from 'drizzle-orm/pg-core' -import { publishersToExternalIds } from './publishersToExternalIds' -import { publishers_to_games } from './publishersToGames' - -export const publishers = pgTable('publishers', { - id: uuid('id').primaryKey().defaultRandom(), - cuid: text('cuid') - .unique() - .$defaultFn(() => cuid2()), - name: text('name'), - slug: text('slug'), - ...timestamps, -}) - -export type Publishers = InferSelectModel - -export const publishers_relations = relations(publishers, ({ many }) => ({ - publishers_to_games: many(publishers_to_games), - publishersToExternalIds: many(publishersToExternalIds), -})) diff --git a/src/lib/server/api/databases/tables/recovery-codes.table.ts b/src/lib/server/api/databases/tables/recovery-codes.table.ts deleted file mode 100644 index d79e469..0000000 --- a/src/lib/server/api/databases/tables/recovery-codes.table.ts +++ /dev/null @@ -1,16 +0,0 @@ -import type { InferSelectModel } from 'drizzle-orm' -import { boolean, pgTable, text, uuid } from 'drizzle-orm/pg-core' -import { timestamps } from '../../common/utils/table' -import { usersTable } from './users.table' - -export const recoveryCodesTable = pgTable('recovery_codes', { - id: uuid('id').primaryKey().defaultRandom(), - userId: uuid('user_id') - .notNull() - .references(() => usersTable.id), - code: text('code').notNull(), - used: boolean('used').default(false), - ...timestamps, -}) - -export type RecoveryCodesTable = InferSelectModel diff --git a/src/lib/server/api/databases/tables/roles.table.ts b/src/lib/server/api/databases/tables/roles.table.ts deleted file mode 100644 index e9f036f..0000000 --- a/src/lib/server/api/databases/tables/roles.table.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { createId as cuid2 } from '@paralleldrive/cuid2' -import { type InferSelectModel, relations } from 'drizzle-orm' -import { pgTable, text, uuid } from 'drizzle-orm/pg-core' -import { timestamps } from '../../common/utils/table' -import { user_roles } from './userRoles.table' - -export enum RoleName { - ADMIN = 'admin', - EDITOR = 'editor', - MODERATOR = 'moderator', - USER = 'user', -} - -export const rolesTable = pgTable('roles', { - id: uuid('id').primaryKey().defaultRandom(), - cuid: text('cuid') - .unique() - .$defaultFn(() => cuid2()) - .notNull(), - name: text('name').unique().notNull(), - ...timestamps, -}) - -export type Roles = InferSelectModel - -export const role_relations = relations(rolesTable, ({ many }) => ({ - user_roles: many(user_roles), -})) diff --git a/src/lib/server/api/databases/tables/two-factor.table.ts b/src/lib/server/api/databases/tables/two-factor.table.ts deleted file mode 100644 index f5e794f..0000000 --- a/src/lib/server/api/databases/tables/two-factor.table.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { createId as cuid2 } from '@paralleldrive/cuid2' -import { type InferSelectModel, relations } from 'drizzle-orm' -import { boolean, pgTable, text, timestamp, uuid } from 'drizzle-orm/pg-core' -import { timestamps } from '../../common/utils/table' -import { usersTable } from './users.table' - -export const twoFactorTable = pgTable('two_factor', { - id: uuid('id').primaryKey().defaultRandom(), - cuid: text('cuid') - .unique() - .$defaultFn(() => cuid2()), - secret: text('secret').notNull(), - enabled: boolean('enabled').notNull().default(false), - initiatedTime: timestamp('initiated_time', { - mode: 'date', - withTimezone: true, - }), - userId: uuid('user_id') - .notNull() - .references(() => usersTable.id) - .unique(), - ...timestamps, -}) - -export const emailVerificationsRelations = relations(twoFactorTable, ({ one }) => ({ - user: one(usersTable, { - fields: [twoFactorTable.userId], - references: [usersTable.id], - }), -})) - -export type TwoFactor = InferSelectModel diff --git a/src/lib/server/api/databases/tables/wishlists.table.ts b/src/lib/server/api/databases/tables/wishlists.table.ts deleted file mode 100644 index 34f7bf8..0000000 --- a/src/lib/server/api/databases/tables/wishlists.table.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { createId as cuid2 } from '@paralleldrive/cuid2' -import { type InferSelectModel, relations } from 'drizzle-orm' -import { pgTable, text, uuid } from 'drizzle-orm/pg-core' -import { timestamps } from '../../common/utils/table' -import { usersTable } from './users.table' - -export const wishlistsTable = pgTable('wishlists', { - id: uuid('id').primaryKey().defaultRandom(), - cuid: text('cuid') - .unique() - .$defaultFn(() => cuid2()), - user_id: uuid('user_id') - .notNull() - .references(() => usersTable.id, { onDelete: 'cascade' }), - name: text('name').notNull().default('My Wishlist'), - ...timestamps, -}) - -export type Wishlists = InferSelectModel - -export const wishlists_relations = relations(wishlistsTable, ({ one }) => ({ - user: one(usersTable, { - fields: [wishlistsTable.user_id], - references: [usersTable.id], - }), -})) diff --git a/src/lib/server/api/middleware/auth.middleware.ts b/src/lib/server/api/middleware/auth.middleware.ts index fc4e076..058df44 100644 --- a/src/lib/server/api/middleware/auth.middleware.ts +++ b/src/lib/server/api/middleware/auth.middleware.ts @@ -1,4 +1,5 @@ -import { LuciaService } from '$lib/server/api/services/lucia.service'; +import { createSessionTokenCookie } from '$lib/server/api/common/utils/cookies'; +import { SessionsService } from '$lib/server/api/services/sessions.service'; import type { MiddlewareHandler } from 'hono'; import { createMiddleware } from 'hono/factory'; import { verifyRequestOrigin } from 'oslo/request'; @@ -6,7 +7,7 @@ import { container } from 'tsyringe'; import type { AppBindings } from '../common/types/hono'; // resolve dependencies from the container -const { lucia } = container.resolve(LuciaService); +const sessionService = container.resolve(SessionsService); export const verifyOrigin: MiddlewareHandler = createMiddleware(async (c, next) => { if (c.req.method === 'GET') { @@ -30,7 +31,7 @@ export const validateAuthSession: MiddlewareHandler = createMiddlew const { session, user } = await lucia.validateSession(sessionId); if (session?.fresh) { - c.header('Set-Cookie', lucia.createSessionCookie(session.id).serialize(), { append: true }); + c.header('Set-Cookie', createSessionTokenCookie(session.id).serialize(), { append: true }); } if (!session) { c.header('Set-Cookie', lucia.createBlankSessionCookie().serialize(), { append: true }); diff --git a/src/lib/server/api/packages/drizzle.ts b/src/lib/server/api/packages/drizzle.ts index c950e4d..e5e5da2 100644 --- a/src/lib/server/api/packages/drizzle.ts +++ b/src/lib/server/api/packages/drizzle.ts @@ -1,22 +1,24 @@ -import { drizzle } from 'drizzle-orm/node-postgres' -import pg from 'pg' -import { config } from '../common/config' -import * as schema from '../databases/tables' +import { drizzle } from 'drizzle-orm/node-postgres'; +import { Pool } from 'pg'; +import { config } from '../common/config'; +import * as schema from '../databases/postgres/tables'; // create the connection -export const pool = new pg.Pool({ - user: config.DATABASE_USER, - password: config.DATABASE_PASSWORD, - host: config.DATABASE_HOST, - port: Number(config.DATABASE_PORT).valueOf(), - database: config.DATABASE_DB, - ssl: config.DATABASE_HOST !== 'localhost', - max: config.DB_MIGRATING || config.DB_SEEDING ? 1 : undefined, -}) +export const pool = new Pool({ + user: config.postgres.user, + password: config.postgres.password, + host: config.postgres.host, + port: Number(config.postgres.port).valueOf(), + database: config.postgres.database, + ssl: config.postgres.host !== 'localhost', + max: config.postgres.migrating || config.postgres.seeding ? 1 : undefined, +}); -export const db = drizzle(pool, { +export const db = drizzle({ + client: pool, + casing: 'snake_case', schema, - logger: config.NODE_ENV === 'development', -}) + logger: !config.isProduction, +}); -export type db = typeof db +export type db = typeof db; diff --git a/src/lib/server/api/repositories/collections.repository.ts b/src/lib/server/api/repositories/collections.repository.ts index 7ce1e71..fddb360 100644 --- a/src/lib/server/api/repositories/collections.repository.ts +++ b/src/lib/server/api/repositories/collections.repository.ts @@ -1,18 +1,18 @@ -import { takeFirstOrThrow } from '$lib/server/api/common/utils/repository' -import { DrizzleService } from '$lib/server/api/services/drizzle.service' -import { type InferInsertModel, eq } from 'drizzle-orm' -import { inject, injectable } from 'tsyringe' -import { collections } from '../databases/tables' +import { takeFirstOrThrow } from '$lib/server/api/common/utils/repository'; +import { DrizzleService } from '$lib/server/api/services/drizzle.service'; +import { type InferInsertModel, eq } from 'drizzle-orm'; +import { inject, injectable } from 'tsyringe'; +import { collections } from '../databases/postgres/tables'; -export type CreateCollection = InferInsertModel -export type UpdateCollection = Partial +export type CreateCollection = InferInsertModel; +export type UpdateCollection = Partial; @injectable() export class CollectionsRepository { constructor(@inject(DrizzleService) private readonly drizzle: DrizzleService) {} async findAll(db = this.drizzle.db) { - return db.query.collections.findMany() + return db.query.collections.findMany(); } async findOneById(id: string, db = this.drizzle.db) { @@ -22,7 +22,7 @@ export class CollectionsRepository { cuid: true, name: true, }, - }) + }); } async findOneByCuid(cuid: string, db = this.drizzle.db) { @@ -32,7 +32,7 @@ export class CollectionsRepository { cuid: true, name: true, }, - }) + }); } async findOneByUserId(userId: string, db = this.drizzle.db) { @@ -42,7 +42,7 @@ export class CollectionsRepository { cuid: true, name: true, }, - }) + }); } async findAllByUserId(userId: string, db = this.drizzle.db) { @@ -53,7 +53,7 @@ export class CollectionsRepository { name: true, createdAt: true, }, - }) + }); } async findAllByUserIdWithDetails(userId: string, db = this.drizzle.db) { @@ -70,14 +70,14 @@ export class CollectionsRepository { }, }, }, - }) + }); } async create(data: CreateCollection, db = this.drizzle.db) { - return db.insert(collections).values(data).returning().then(takeFirstOrThrow) + return db.insert(collections).values(data).returning().then(takeFirstOrThrow); } async update(id: string, data: UpdateCollection, db = this.drizzle.db) { - return db.update(collections).set(data).where(eq(collections.id, id)).returning().then(takeFirstOrThrow) + return db.update(collections).set(data).where(eq(collections.id, id)).returning().then(takeFirstOrThrow); } } diff --git a/src/lib/server/api/repositories/credentials.repository.ts b/src/lib/server/api/repositories/credentials.repository.ts index bdd8de5..4060aa0 100644 --- a/src/lib/server/api/repositories/credentials.repository.ts +++ b/src/lib/server/api/repositories/credentials.repository.ts @@ -1,13 +1,13 @@ -import 'reflect-metadata' -import { CredentialsType, credentialsTable } from '$lib/server/api/databases/tables/credentials.table' -import { DrizzleService } from '$lib/server/api/services/drizzle.service' -import { type InferInsertModel, and, eq } from 'drizzle-orm' -import { inject, injectable } from 'tsyringe' -import { takeFirstOrThrow } from '../common/utils/repository' +import 'reflect-metadata'; +import { CredentialsType, credentialsTable } from '$lib/server/api/databases/postgres/tables/credentials.table'; +import { DrizzleService } from '$lib/server/api/services/drizzle.service'; +import { type InferInsertModel, and, eq } from 'drizzle-orm'; +import { inject, injectable } from 'tsyringe'; +import { takeFirstOrThrow } from '../common/utils/repository'; -export type CreateCredentials = InferInsertModel -export type UpdateCredentials = Partial -export type DeleteCredentials = Pick +export type CreateCredentials = InferInsertModel; +export type UpdateCredentials = Partial; +export type DeleteCredentials = Pick; @injectable() export class CredentialsRepository { @@ -16,56 +16,56 @@ export class CredentialsRepository { async findOneByUserId(userId: string, db = this.drizzle.db) { return db.query.credentialsTable.findFirst({ where: eq(credentialsTable.user_id, userId), - }) + }); } async findOneByUserIdAndType(userId: string, type: CredentialsType, db = this.drizzle.db) { return db.query.credentialsTable.findFirst({ where: and(eq(credentialsTable.user_id, userId), eq(credentialsTable.type, type)), - }) + }); } async findPasswordCredentialsByUserId(userId: string, db = this.drizzle.db) { return db.query.credentialsTable.findFirst({ where: and(eq(credentialsTable.user_id, userId), eq(credentialsTable.type, CredentialsType.PASSWORD)), - }) + }); } async findTOTPCredentialsByUserId(userId: string, db = this.drizzle.db) { return db.query.credentialsTable.findFirst({ where: and(eq(credentialsTable.user_id, userId), eq(credentialsTable.type, CredentialsType.TOTP)), - }) + }); } async findOneById(id: string, db = this.drizzle.db) { return db.query.credentialsTable.findFirst({ where: eq(credentialsTable.id, id), - }) + }); } async findOneByIdOrThrow(id: string, db = this.drizzle.db) { - const credentials = await this.findOneById(id, db) - if (!credentials) throw Error('Credentials not found') - return credentials + const credentials = await this.findOneById(id, db); + if (!credentials) throw Error('Credentials not found'); + return credentials; } async create(data: CreateCredentials, db = this.drizzle.db) { - return db.insert(credentialsTable).values(data).returning().then(takeFirstOrThrow) + return db.insert(credentialsTable).values(data).returning().then(takeFirstOrThrow); } async update(id: string, data: UpdateCredentials, db = this.drizzle.db) { - return db.update(credentialsTable).set(data).where(eq(credentialsTable.id, id)).returning().then(takeFirstOrThrow) + return db.update(credentialsTable).set(data).where(eq(credentialsTable.id, id)).returning().then(takeFirstOrThrow); } async delete(id: string, db = this.drizzle.db) { - return db.delete(credentialsTable).where(eq(credentialsTable.id, id)) + return db.delete(credentialsTable).where(eq(credentialsTable.id, id)); } async deleteByUserId(userId: string, db = this.drizzle.db) { - return db.delete(credentialsTable).where(eq(credentialsTable.user_id, userId)) + return db.delete(credentialsTable).where(eq(credentialsTable.user_id, userId)); } async deleteByUserIdAndType(userId: string, type: CredentialsType, db = this.drizzle.db) { - return db.delete(credentialsTable).where(and(eq(credentialsTable.user_id, userId), eq(credentialsTable.type, type))) + return db.delete(credentialsTable).where(and(eq(credentialsTable.user_id, userId), eq(credentialsTable.type, type))); } } diff --git a/src/lib/server/api/repositories/federated_identity.repository.ts b/src/lib/server/api/repositories/federated_identity.repository.ts index 2180590..97ff4f3 100644 --- a/src/lib/server/api/repositories/federated_identity.repository.ts +++ b/src/lib/server/api/repositories/federated_identity.repository.ts @@ -1,10 +1,10 @@ -import { type InferInsertModel, and, eq } from 'drizzle-orm' -import { inject, injectable } from 'tsyringe' -import { takeFirstOrThrow } from '../common/utils/repository' -import { federatedIdentityTable } from '../databases/tables' -import { DrizzleService } from '../services/drizzle.service' +import { type InferInsertModel, and, eq } from 'drizzle-orm'; +import { inject, injectable } from 'tsyringe'; +import { takeFirstOrThrow } from '../common/utils/repository'; +import { federatedIdentityTable } from '../databases/postgres/tables'; +import { DrizzleService } from '../services/drizzle.service'; -export type CreateFederatedIdentity = InferInsertModel +export type CreateFederatedIdentity = InferInsertModel; @injectable() export class FederatedIdentityRepository { @@ -13,16 +13,16 @@ export class FederatedIdentityRepository { async findOneByUserIdAndProvider(userId: string, provider: string) { return this.drizzle.db.query.federatedIdentityTable.findFirst({ where: and(eq(federatedIdentityTable.user_id, userId), eq(federatedIdentityTable.identity_provider, provider)), - }) + }); } async findOneByFederatedUserIdAndProvider(federatedUserId: string, provider: string) { return this.drizzle.db.query.federatedIdentityTable.findFirst({ where: and(eq(federatedIdentityTable.federated_user_id, federatedUserId), eq(federatedIdentityTable.identity_provider, provider)), - }) + }); } async create(data: CreateFederatedIdentity, db = this.drizzle.db) { - return db.insert(federatedIdentityTable).values(data).returning().then(takeFirstOrThrow) + return db.insert(federatedIdentityTable).values(data).returning().then(takeFirstOrThrow); } } diff --git a/src/lib/server/api/repositories/recovery-codes.repository.ts b/src/lib/server/api/repositories/recovery-codes.repository.ts index 9d9e720..8819585 100644 --- a/src/lib/server/api/repositories/recovery-codes.repository.ts +++ b/src/lib/server/api/repositories/recovery-codes.repository.ts @@ -1,27 +1,27 @@ -import 'reflect-metadata' -import { takeFirstOrThrow } from '$lib/server/api/common/utils/repository' -import { DrizzleService } from '$lib/server/api/services/drizzle.service' -import { type InferInsertModel, eq } from 'drizzle-orm' -import { inject, injectable } from 'tsyringe' -import { recoveryCodesTable } from '../databases/tables' +import 'reflect-metadata'; +import { takeFirstOrThrow } from '$lib/server/api/common/utils/repository'; +import { DrizzleService } from '$lib/server/api/services/drizzle.service'; +import { type InferInsertModel, eq } from 'drizzle-orm'; +import { inject, injectable } from 'tsyringe'; +import { recoveryCodesTable } from '../databases/postgres/tables'; -export type CreateRecoveryCodes = InferInsertModel +export type CreateRecoveryCodes = InferInsertModel; @injectable() export class RecoveryCodesRepository { constructor(@inject(DrizzleService) private readonly drizzle: DrizzleService) {} async create(data: CreateRecoveryCodes, db = this.drizzle.db) { - return db.insert(recoveryCodesTable).values(data).returning().then(takeFirstOrThrow) + return db.insert(recoveryCodesTable).values(data).returning().then(takeFirstOrThrow); } async findAllByUserId(userId: string, db = this.drizzle.db) { return db.query.recoveryCodesTable.findMany({ where: eq(recoveryCodesTable.userId, userId), - }) + }); } async deleteAllByUserId(userId: string, db = this.drizzle.db) { - return db.delete(recoveryCodesTable).where(eq(recoveryCodesTable.userId, userId)) + return db.delete(recoveryCodesTable).where(eq(recoveryCodesTable.userId, userId)); } } diff --git a/src/lib/server/api/repositories/roles.repository.ts b/src/lib/server/api/repositories/roles.repository.ts index 4d9f283..f3b45df 100644 --- a/src/lib/server/api/repositories/roles.repository.ts +++ b/src/lib/server/api/repositories/roles.repository.ts @@ -1,8 +1,8 @@ -import { DrizzleService } from '$lib/server/api/services/drizzle.service' -import { type InferInsertModel, eq } from 'drizzle-orm' -import { inject, injectable } from 'tsyringe' -import { takeFirstOrThrow } from '../common/utils/repository' -import { rolesTable } from '../databases/tables' +import { DrizzleService } from '$lib/server/api/services/drizzle.service'; +import { type InferInsertModel, eq } from 'drizzle-orm'; +import { inject, injectable } from 'tsyringe'; +import { takeFirstOrThrow } from '../common/utils/repository'; +import { rolesTable } from '../databases/postgres/tables'; /* -------------------------------------------------------------------------- */ /* Repository */ @@ -20,8 +20,8 @@ storing data. They should not contain any business logic, only database queries. In our case the method 'trxHost' is used to set the transaction context. */ -export type CreateRole = InferInsertModel -export type UpdateRole = Partial +export type CreateRole = InferInsertModel; +export type UpdateRole = Partial; @injectable() export class RolesRepository { @@ -30,40 +30,40 @@ export class RolesRepository { async findOneById(id: string, db = this.drizzle.db) { return db.query.rolesTable.findFirst({ where: eq(rolesTable.id, id), - }) + }); } async findOneByIdOrThrow(id: string, db = this.drizzle.db) { - const role = await this.findOneById(id, db) - if (!role) throw Error('Role not found') - return role + const role = await this.findOneById(id, db); + if (!role) throw Error('Role not found'); + return role; } async findAll(db = this.drizzle.db) { - return db.query.rolesTable.findMany() + return db.query.rolesTable.findMany(); } async findOneByName(name: string, db = this.drizzle.db) { return db.query.rolesTable.findFirst({ where: eq(rolesTable.name, name), - }) + }); } async findOneByNameOrThrow(name: string, db = this.drizzle.db) { - const role = await this.findOneByName(name, db) - if (!role) throw Error('Role not found') - return role + const role = await this.findOneByName(name, db); + if (!role) throw Error('Role not found'); + return role; } async create(data: CreateRole, db = this.drizzle.db) { - return db.insert(rolesTable).values(data).returning().then(takeFirstOrThrow) + return db.insert(rolesTable).values(data).returning().then(takeFirstOrThrow); } async update(id: string, data: UpdateRole, db = this.drizzle.db) { - return db.update(rolesTable).set(data).where(eq(rolesTable.id, id)).returning().then(takeFirstOrThrow) + return db.update(rolesTable).set(data).where(eq(rolesTable.id, id)).returning().then(takeFirstOrThrow); } async delete(id: string, db = this.drizzle.db) { - return db.delete(rolesTable).where(eq(rolesTable.id, id)).returning().then(takeFirstOrThrow) + return db.delete(rolesTable).where(eq(rolesTable.id, id)).returning().then(takeFirstOrThrow); } } diff --git a/src/lib/server/api/repositories/sessions.repository.ts b/src/lib/server/api/repositories/sessions.repository.ts new file mode 100644 index 0000000..7da6b5f --- /dev/null +++ b/src/lib/server/api/repositories/sessions.repository.ts @@ -0,0 +1,33 @@ +import 'reflect-metadata'; +import { takeFirstOrThrow } from '$lib/server/api/common/utils/repository'; +import { DrizzleService } from '$lib/server/api/services/drizzle.service'; +import { type InferInsertModel, eq } from 'drizzle-orm'; +import { inject, injectable } from 'tsyringe'; +import { sessionsTable, usersTable } from '../databases/postgres/tables'; + +export type CreateSession = InferInsertModel; + +@injectable() +export class SessionsRepository { + constructor(@inject(DrizzleService) private readonly drizzle: DrizzleService) {} + + async create(data: CreateSession, db = this.drizzle.db) { + return db.insert(sessionsTable).values(data).returning().then(takeFirstOrThrow); + } + + async findBySessionId(sessionId: string, db = this.drizzle.db) { + return await db + .select({ user: usersTable, session: sessionsTable }) + .from(sessionsTable) + .innerJoin(usersTable, eq(sessionsTable.userId, usersTable.id)) + .where(eq(sessionsTable.id, sessionId)); + } + + async deleteBySessionId(sessionId: string, db = this.drizzle.db) { + return db.delete(sessionsTable).where(eq(sessionsTable.id, sessionId)); + } + + async updateSessionExpiresAt(sessionId: string, expiresAt: Date, db = this.drizzle.db) { + db.update(sessionsTable).set({ expiresAt }).where(eq(sessionsTable.id, sessionId)); + } +} diff --git a/src/lib/server/api/repositories/user_roles.repository.ts b/src/lib/server/api/repositories/user_roles.repository.ts index ef95b74..eb9ee68 100644 --- a/src/lib/server/api/repositories/user_roles.repository.ts +++ b/src/lib/server/api/repositories/user_roles.repository.ts @@ -1,8 +1,8 @@ -import { DrizzleService } from '$lib/server/api/services/drizzle.service' -import { type InferInsertModel, eq } from 'drizzle-orm' -import { inject, injectable } from 'tsyringe' -import { takeFirstOrThrow } from '../common/utils/repository' -import { user_roles } from '../databases/tables' +import { DrizzleService } from '$lib/server/api/services/drizzle.service'; +import { type InferInsertModel, eq } from 'drizzle-orm'; +import { inject, injectable } from 'tsyringe'; +import { takeFirstOrThrow } from '../common/utils/repository'; +import { user_roles } from '../databases/postgres/tables'; /* -------------------------------------------------------------------------- */ /* Repository */ @@ -20,8 +20,8 @@ storing data. They should not contain any business logic, only database queries. In our case the method 'trxHost' is used to set the transaction context. */ -export type CreateUserRole = InferInsertModel -export type UpdateUserRole = Partial +export type CreateUserRole = InferInsertModel; +export type UpdateUserRole = Partial; @injectable() export class UserRolesRepository { @@ -30,26 +30,26 @@ export class UserRolesRepository { async findOneById(id: string, db = this.drizzle.db) { return db.query.user_roles.findFirst({ where: eq(user_roles.id, id), - }) + }); } async findOneByIdOrThrow(id: string, db = this.drizzle.db) { - const userRole = await this.findOneById(id, db) - if (!userRole) throw Error('User not found') - return userRole + const userRole = await this.findOneById(id, db); + if (!userRole) throw Error('User not found'); + return userRole; } async findAllByUserId(userId: string, db = this.drizzle.db) { return db.query.user_roles.findMany({ where: eq(user_roles.user_id, userId), - }) + }); } async create(data: CreateUserRole, db = this.drizzle.db) { - return db.insert(user_roles).values(data).returning().then(takeFirstOrThrow) + return db.insert(user_roles).values(data).returning().then(takeFirstOrThrow); } async delete(id: string, db = this.drizzle.db) { - return db.delete(user_roles).where(eq(user_roles.id, id)).returning().then(takeFirstOrThrow) + return db.delete(user_roles).where(eq(user_roles.id, id)).returning().then(takeFirstOrThrow); } } diff --git a/src/lib/server/api/repositories/users.repository.ts b/src/lib/server/api/repositories/users.repository.ts index 4ee1ba3..b753ee3 100644 --- a/src/lib/server/api/repositories/users.repository.ts +++ b/src/lib/server/api/repositories/users.repository.ts @@ -1,4 +1,4 @@ -import { usersTable } from '$lib/server/api/databases/tables/users.table'; +import { usersTable } from '$lib/server/api/databases/postgres/tables/users.table'; import { DrizzleService } from '$lib/server/api/services/drizzle.service'; import { type InferInsertModel, eq } from 'drizzle-orm'; import { inject, injectable } from 'tsyringe'; diff --git a/src/lib/server/api/repositories/wishlists.repository.ts b/src/lib/server/api/repositories/wishlists.repository.ts index 112f272..4a40eef 100644 --- a/src/lib/server/api/repositories/wishlists.repository.ts +++ b/src/lib/server/api/repositories/wishlists.repository.ts @@ -1,18 +1,18 @@ -import { DrizzleService } from '$lib/server/api/services/drizzle.service' -import { type InferInsertModel, eq } from 'drizzle-orm' -import { inject, injectable } from 'tsyringe' -import { takeFirstOrThrow } from '../common/utils/repository' -import { wishlistsTable } from '../databases/tables' +import { DrizzleService } from '$lib/server/api/services/drizzle.service'; +import { type InferInsertModel, eq } from 'drizzle-orm'; +import { inject, injectable } from 'tsyringe'; +import { takeFirstOrThrow } from '../common/utils/repository'; +import { wishlistsTable } from '../databases/postgres/tables'; -export type CreateWishlist = InferInsertModel -export type UpdateWishlist = Partial +export type CreateWishlist = InferInsertModel; +export type UpdateWishlist = Partial; @injectable() export class WishlistsRepository { constructor(@inject(DrizzleService) private readonly drizzle: DrizzleService) {} async findAll(db = this.drizzle.db) { - return db.query.wishlistsTable.findMany() + return db.query.wishlistsTable.findMany(); } async findOneById(id: string, db = this.drizzle.db) { @@ -22,7 +22,7 @@ export class WishlistsRepository { cuid: true, name: true, }, - }) + }); } async findOneByCuid(cuid: string, db = this.drizzle.db) { @@ -32,7 +32,7 @@ export class WishlistsRepository { cuid: true, name: true, }, - }) + }); } async findOneByUserId(userId: string, db = this.drizzle.db) { @@ -42,7 +42,7 @@ export class WishlistsRepository { cuid: true, name: true, }, - }) + }); } async findAllByUserId(userId: string, db = this.drizzle.db) { @@ -53,14 +53,14 @@ export class WishlistsRepository { name: true, createdAt: true, }, - }) + }); } async create(data: CreateWishlist, db = this.drizzle.db) { - return db.insert(wishlistsTable).values(data).returning().then(takeFirstOrThrow) + return db.insert(wishlistsTable).values(data).returning().then(takeFirstOrThrow); } async update(id: string, data: UpdateWishlist, db = this.drizzle.db) { - return db.update(wishlistsTable).set(data).where(eq(wishlistsTable.id, id)).returning().then(takeFirstOrThrow) + return db.update(wishlistsTable).set(data).where(eq(wishlistsTable.id, id)).returning().then(takeFirstOrThrow); } } diff --git a/src/lib/server/api/services/drizzle.service.ts b/src/lib/server/api/services/drizzle.service.ts index 29e1f8a..0811dc9 100644 --- a/src/lib/server/api/services/drizzle.service.ts +++ b/src/lib/server/api/services/drizzle.service.ts @@ -3,7 +3,7 @@ import { type NodePgDatabase, drizzle } from 'drizzle-orm/node-postgres'; import pg from 'pg'; import { type Disposable, injectable } from 'tsyringe'; import { config } from '../common/config'; -import * as schema from '../databases/tables'; +import * as schema from '../databases/postgres/tables'; @injectable() export class DrizzleService implements Disposable { diff --git a/src/lib/server/api/services/iam.service.ts b/src/lib/server/api/services/iam.service.ts index 7d93a5f..d6f6103 100644 --- a/src/lib/server/api/services/iam.service.ts +++ b/src/lib/server/api/services/iam.service.ts @@ -1,11 +1,11 @@ -import { CredentialsType } from '$lib/server/api/databases/tables' -import type { ChangePasswordDto } from '$lib/server/api/dtos/change-password.dto' -import type { UpdateEmailDto } from '$lib/server/api/dtos/update-email.dto' -import type { UpdateProfileDto } from '$lib/server/api/dtos/update-profile.dto' -import type { VerifyPasswordDto } from '$lib/server/api/dtos/verify-password.dto' -import { LuciaService } from '$lib/server/api/services/lucia.service' -import { UsersService } from '$lib/server/api/services/users.service' -import { inject, injectable } from 'tsyringe' +import type { ChangePasswordDto } from '$lib/server/api/dtos/change-password.dto'; +import type { UpdateEmailDto } from '$lib/server/api/dtos/update-email.dto'; +import type { UpdateProfileDto } from '$lib/server/api/dtos/update-profile.dto'; +import type { VerifyPasswordDto } from '$lib/server/api/dtos/verify-password.dto'; +import { SessionsService } from '$lib/server/api/services/sessions.service'; +import { UsersService } from '$lib/server/api/services/users.service'; +import { inject, injectable } from 'tsyringe'; +import { CredentialsType } from '../databases/postgres/tables'; /* -------------------------------------------------------------------------- */ /* Service */ @@ -27,60 +27,60 @@ simple as possible. This makes the service easier to read, test and understand. @injectable() export class IamService { constructor( - @inject(LuciaService) private luciaService: LuciaService, + @inject(SessionsService) private luciaService: SessionsService, @inject(UsersService) private readonly usersService: UsersService, ) {} async logout(sessionId: string) { - return this.luciaService.lucia.invalidateSession(sessionId) + return this.luciaService.lucia.invalidateSession(sessionId); } async updateProfile(userId: string, data: UpdateProfileDto) { - const user = await this.usersService.findOneById(userId) + const user = await this.usersService.findOneById(userId); if (!user) { return { error: 'User not found', - } + }; } - const existingUserForNewUsername = await this.usersService.findOneByUsername(data.username) + const existingUserForNewUsername = await this.usersService.findOneByUsername(data.username); if (existingUserForNewUsername && existingUserForNewUsername.id !== user.id) { return { error: 'Username already in use', - } + }; } return this.usersService.updateUser(user.id, { first_name: data.firstName, last_name: data.lastName, username: data.username !== user.username ? data.username : user.username, - }) + }); } async updateEmail(userId: string, data: UpdateEmailDto) { - const { email } = data + const { email } = data; - const existingUserEmail = await this.usersService.findOneByEmail(email) + const existingUserEmail = await this.usersService.findOneByEmail(email); if (existingUserEmail && existingUserEmail.id !== userId) { - return null + return null; } return this.usersService.updateUser(userId, { email, - }) + }); } async updatePassword(userId: string, data: ChangePasswordDto) { - const { password } = data - await this.usersService.updatePassword(userId, password) + const { password } = data; + await this.usersService.updatePassword(userId, password); } async verifyPassword(userId: string, data: VerifyPasswordDto) { - const user = await this.usersService.findOneById(userId) + const user = await this.usersService.findOneById(userId); if (!user) { - return null + return null; } - const { password } = data - return this.usersService.verifyPassword(userId, { password }) + const { password } = data; + return this.usersService.verifyPassword(userId, { password }); } } diff --git a/src/lib/server/api/services/loginrequest.service.ts b/src/lib/server/api/services/loginrequest.service.ts index 4f93c9c..79c4030 100644 --- a/src/lib/server/api/services/loginrequest.service.ts +++ b/src/lib/server/api/services/loginrequest.service.ts @@ -1,19 +1,19 @@ -import type { SigninUsernameDto } from '$lib/server/api/dtos/signin-username.dto' -import { LuciaService } from '$lib/server/api/services/lucia.service' -import type { HonoRequest } from 'hono' -import { inject, injectable } from 'tsyringe' -import { BadRequest } from '../common/exceptions' -import type { Credentials } from '../databases/tables' -import { DatabaseProvider } from '../providers/database.provider' -import { CredentialsRepository } from '../repositories/credentials.repository' -import { UsersRepository } from '../repositories/users.repository' -import { MailerService } from './mailer.service' -import { TokensService } from './tokens.service' +import type { SigninUsernameDto } from '$lib/server/api/dtos/signin-username.dto'; +import { SessionsService } from '$lib/server/api/services/sessions.service'; +import type { HonoRequest } from 'hono'; +import { inject, injectable } from 'tsyringe'; +import { BadRequest } from '../common/exceptions'; +import type { Credentials } from '../databases/postgres/tables'; +import { DatabaseProvider } from '../providers/database.provider'; +import { CredentialsRepository } from '../repositories/credentials.repository'; +import { UsersRepository } from '../repositories/users.repository'; +import { MailerService } from './mailer.service'; +import { TokensService } from './tokens.service'; @injectable() export class LoginRequestsService { constructor( - @inject(LuciaService) private luciaService: LuciaService, + @inject(SessionsService) private sessionsService: SessionsService, @inject(DatabaseProvider) private readonly db: DatabaseProvider, @inject(TokensService) private readonly tokensService: TokensService, @inject(MailerService) private readonly mailerService: MailerService, @@ -34,46 +34,48 @@ export class LoginRequestsService { // } async verify(data: SigninUsernameDto, req: HonoRequest) { - const requestIpAddress = req.header('x-real-ip') - const requestIpCountry = req.header('x-vercel-ip-country') - const existingUser = await this.usersRepository.findOneByUsername(data.username) + const requestIpAddress = req.header('x-real-ip'); + const requestIpCountry = req.header('x-vercel-ip-country'); + const existingUser = await this.usersRepository.findOneByUsername(data.username); if (!existingUser) { - throw BadRequest('User not found') + throw BadRequest('User not found'); } - const credential = await this.credentialsRepository.findPasswordCredentialsByUserId(existingUser.id) + const credential = await this.credentialsRepository.findPasswordCredentialsByUserId(existingUser.id); if (!credential) { - throw BadRequest('Invalid credentials') + throw BadRequest('Invalid credentials'); } if (!(await this.tokensService.verifyHashedToken(credential.secret_data, data.password))) { - throw BadRequest('Invalid credentials') + throw BadRequest('Invalid credentials'); } - const totpCredentials = await this.credentialsRepository.findTOTPCredentialsByUserId(existingUser.id) + const totpCredentials = await this.credentialsRepository.findTOTPCredentialsByUserId(existingUser.id); - return await this.createUserSession(existingUser.id, req, totpCredentials) + return await this.createUserSession(existingUser.id, req, totpCredentials); } async createUserSession(existingUserId: string, req: HonoRequest, totpCredentials: Credentials | undefined) { - const requestIpAddress = req.header('x-real-ip') - const requestIpCountry = req.header('x-vercel-ip-country') - return this.luciaService.lucia.createSession(existingUserId, { - ip_country: requestIpCountry || 'unknown', - ip_address: requestIpAddress || 'unknown', - twoFactorAuthEnabled: !!totpCredentials && totpCredentials?.secret_data !== null && totpCredentials?.secret_data !== '', - isTwoFactorAuthenticated: false, - }) + const requestIpAddress = req.header('x-real-ip'); + const requestIpCountry = req.header('x-vercel-ip-country'); + return this.sessionsService.createSession( + this.sessionsService.generateSessionToken(), + existingUserId, + requestIpCountry || 'unknown', + requestIpAddress || 'unknown', + !!totpCredentials && totpCredentials?.secret_data !== null && totpCredentials?.secret_data !== '', + false, + ); } // Create a new user and send a welcome email - or other onboarding process private async handleNewUserRegistration(email: string) { - const newUser = await this.usersRepository.create({ email, verified: true }) - this.mailerService.sendWelcome({ to: email, props: null }) + const newUser = await this.usersRepository.create({ email, verified: true }); + this.mailerService.sendWelcome({ to: email, props: null }); // TODO: add whatever onboarding process or extra data you need here - return newUser + return newUser; } // Fetch a valid request from the database, verify the token and burn the request if it is valid diff --git a/src/lib/server/api/services/lucia.service.ts b/src/lib/server/api/services/lucia.service.ts deleted file mode 100644 index 05c5345..0000000 --- a/src/lib/server/api/services/lucia.service.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { config } from '$lib/server/api/common/config' -import { DrizzleService } from '$lib/server/api/services/drizzle.service' -import { DrizzlePostgreSQLAdapter } from '@lucia-auth/adapter-drizzle' -import { Lucia, TimeSpan } from 'lucia' -import { inject, injectable } from 'tsyringe' - -@injectable() -export class LuciaService { - readonly lucia: Lucia - constructor(@inject(DrizzleService) private readonly drizzle: DrizzleService) { - const adapter = new DrizzlePostgreSQLAdapter(this.drizzle.db, this.drizzle.schema.sessionsTable, this.drizzle.schema.usersTable) - this.lucia = new Lucia(adapter, { - sessionExpiresIn: new TimeSpan(2, 'w'), // 2 weeks - sessionCookie: { - name: 'session', - expires: false, // session cookies have very long lifespan (2 years) - attributes: { - // set to `true` when using HTTPS - secure: config.isProduction, - sameSite: 'strict', - domain: config.domain, - }, - }, - getSessionAttributes: (attributes) => { - return { - ipCountry: attributes.ip_country, - ipAddress: attributes.ip_address, - isTwoFactorAuthEnabled: attributes.twoFactorAuthEnabled, - isTwoFactorAuthenticated: attributes.isTwoFactorAuthenticated, - } - }, - getUserAttributes: (attributes) => { - return { - // ...attributes, - username: attributes.username, - email: attributes.email, - firstName: attributes.first_name, - lastName: attributes.last_name, - mfa_enabled: attributes.mfa_enabled, - theme: attributes.theme, - } - }, - }) - } -} diff --git a/src/lib/server/api/services/sessions.service.ts b/src/lib/server/api/services/sessions.service.ts new file mode 100644 index 0000000..73356c7 --- /dev/null +++ b/src/lib/server/api/services/sessions.service.ts @@ -0,0 +1,71 @@ +import { SessionsRepository } from '$lib/server/api/repositories/sessions.repository'; +import { sha256 } from '@oslojs/crypto/sha2'; +import { encodeBase32LowerCaseNoPadding, encodeHexLowerCase } from '@oslojs/encoding'; +import { TimeSpan } from 'lucia'; +import { inject, injectable } from 'tsyringe'; +import type { Sessions, Users } from '../databases/postgres/tables'; + +export type SessionValidationResult = { session: Sessions; user: Users } | { session: null; user: null }; + +@injectable() +export class SessionsService { + constructor(@inject(SessionsRepository) private readonly sessionsRepository: SessionsRepository) {} + + generateSessionToken() { + const bytes = new Uint8Array(20); + crypto.getRandomValues(bytes); + return encodeBase32LowerCaseNoPadding(bytes); + } + + async createSession( + token: string, + userId: string, + ipCountry: string, + ipAddress: string, + twoFactorAuthEnabled: boolean, + isTwoFactorAuthenticated: boolean, + ) { + const sessionId = encodeHexLowerCase(sha256(new TextEncoder().encode(token))); + const session: Sessions = { + id: sessionId, + userId, + expiresAt: new Date(Date.now() + new TimeSpan(2, 'w').seconds()), + ipCountry, + ipAddress, + twoFactorAuthEnabled, + isTwoFactorAuthenticated, + }; + await this.sessionsRepository.create(session); + return session; + } + + async validateSessionToken(token: string): Promise { + const sessionId = encodeHexLowerCase(sha256(new TextEncoder().encode(token))); + const sessions = await this.sessionsRepository.findBySessionId(sessionId); + if (sessions.length < 1) { + return { + session: null, + user: null, + }; + } + const { user, session } = sessions[0]; + if (Date.now() >= session.expiresAt.getTime()) { + await this.sessionsRepository.deleteBySessionId(sessionId); + return { + session: null, + user: null, + }; + } + + if (Date.now() >= session.expiresAt.getTime() - new TimeSpan(1, 'w').seconds()) { + session.expiresAt = new Date(Date.now() + new TimeSpan(2, 'w').seconds()); + await this.sessionsRepository.updateSessionExpiresAt(sessionId, session.expiresAt); + } + + return { session, user }; + } + + async invalidateSession(sessionId: string) { + await this.sessionsRepository.deleteBySessionId(sessionId); + } +} diff --git a/src/lib/server/api/services/totp.service.ts b/src/lib/server/api/services/totp.service.ts index 6e8b7df..5bf5ed7 100644 --- a/src/lib/server/api/services/totp.service.ts +++ b/src/lib/server/api/services/totp.service.ts @@ -2,7 +2,7 @@ import { CredentialsRepository } from '$lib/server/api/repositories/credentials. import { decodeHex, encodeHexLowerCase } from '@oslojs/encoding'; import { verifyTOTP } from '@oslojs/otp'; import { inject, injectable } from 'tsyringe'; -import type { CredentialsType } from '../databases/tables'; +import type { CredentialsType } from '../databases/postgres/tables'; @injectable() export class TotpService { diff --git a/src/lib/server/api/services/users.service.ts b/src/lib/server/api/services/users.service.ts index 172908f..c2870e4 100644 --- a/src/lib/server/api/services/users.service.ts +++ b/src/lib/server/api/services/users.service.ts @@ -1,16 +1,16 @@ -import type { SignupUsernameEmailDto } from '$lib/server/api/dtos/signup-username-email.dto' -import { CredentialsRepository } from '$lib/server/api/repositories/credentials.repository' -import { FederatedIdentityRepository } from '$lib/server/api/repositories/federated_identity.repository' -import { WishlistsRepository } from '$lib/server/api/repositories/wishlists.repository' -import { TokensService } from '$lib/server/api/services/tokens.service' -import { UserRolesService } from '$lib/server/api/services/user_roles.service' -import { inject, injectable } from 'tsyringe' -import {CredentialsType, RoleName} from '../databases/tables' -import { type UpdateUser, UsersRepository } from '../repositories/users.repository' -import { CollectionsService } from './collections.service' -import { DrizzleService } from './drizzle.service' -import { WishlistsService } from './wishlists.service' -import type {OAuthUser} from "$lib/server/api/common/types/oauth"; +import type { OAuthUser } from '$lib/server/api/common/types/oauth'; +import type { SignupUsernameEmailDto } from '$lib/server/api/dtos/signup-username-email.dto'; +import { CredentialsRepository } from '$lib/server/api/repositories/credentials.repository'; +import { FederatedIdentityRepository } from '$lib/server/api/repositories/federated_identity.repository'; +import { WishlistsRepository } from '$lib/server/api/repositories/wishlists.repository'; +import { TokensService } from '$lib/server/api/services/tokens.service'; +import { UserRolesService } from '$lib/server/api/services/user_roles.service'; +import { inject, injectable } from 'tsyringe'; +import { CredentialsType, RoleName } from '../databases/postgres/tables'; +import { type UpdateUser, UsersRepository } from '../repositories/users.repository'; +import { CollectionsService } from './collections.service'; +import { DrizzleService } from './drizzle.service'; +import { WishlistsService } from './wishlists.service'; @injectable() export class UsersService { @@ -27,9 +27,9 @@ export class UsersService { ) {} async create(data: SignupUsernameEmailDto) { - const { firstName, lastName, email, username, password } = data + const { firstName, lastName, email, username, password } = data; - const hashedPassword = await this.tokenService.createHashedToken(password) + const hashedPassword = await this.tokenService.createHashedToken(password); return await this.drizzleService.db.transaction(async (trx) => { const createdUser = await this.usersRepository.create( { @@ -39,10 +39,10 @@ export class UsersService { username, }, trx, - ) + ); if (!createdUser) { - return null + return null; } const credentials = await this.credentialsRepository.create( @@ -52,18 +52,18 @@ export class UsersService { secret_data: hashedPassword, }, trx, - ) + ); if (!credentials) { - await this.usersRepository.delete(createdUser.id) - return null + await this.usersRepository.delete(createdUser.id); + return null; } - await this.userRolesService.addRoleToUser(createdUser.id, RoleName.USER, true, trx) + await this.userRolesService.addRoleToUser(createdUser.id, RoleName.USER, true, trx); - await this.wishlistsService.createEmptyNoName(createdUser.id, trx) - await this.collectionsService.createEmptyNoName(createdUser.id, trx) - }) + await this.wishlistsService.createEmptyNoName(createdUser.id, trx); + await this.collectionsService.createEmptyNoName(createdUser.id, trx); + }); } async createOAuthUser(oAuthUser: OAuthUser, oauthProvider: string) { @@ -78,10 +78,10 @@ export class UsersService { email_verified: oAuthUser.email_verified || false, }, trx, - ) + ); if (!createdUser) { - return null + return null; } await this.federatedIdentityRepository.create( @@ -92,58 +92,58 @@ export class UsersService { federated_username: oAuthUser.email || oAuthUser.username, }, trx, - ) + ); - await this.userRolesService.addRoleToUser(createdUser.id, RoleName.USER, true, trx) + await this.userRolesService.addRoleToUser(createdUser.id, RoleName.USER, true, trx); - await this.wishlistsService.createEmptyNoName(createdUser.id, trx) - await this.collectionsService.createEmptyNoName(createdUser.id, trx) - return createdUser - }) + await this.wishlistsService.createEmptyNoName(createdUser.id, trx); + await this.collectionsService.createEmptyNoName(createdUser.id, trx); + return createdUser; + }); } async updateUser(userId: string, data: UpdateUser) { - return this.usersRepository.update(userId, data) + return this.usersRepository.update(userId, data); } async findOneByUsername(username: string) { - return this.usersRepository.findOneByUsername(username) + return this.usersRepository.findOneByUsername(username); } async findOneByEmail(email: string) { - return this.usersRepository.findOneByEmail(email) + return this.usersRepository.findOneByEmail(email); } async findOneById(id: string) { - return this.usersRepository.findOneById(id) + return this.usersRepository.findOneById(id); } async updatePassword(userId: string, password: string) { - const hashedPassword = await this.tokenService.createHashedToken(password) - const currentCredentials = await this.credentialsRepository.findPasswordCredentialsByUserId(userId) + const hashedPassword = await this.tokenService.createHashedToken(password); + const currentCredentials = await this.credentialsRepository.findPasswordCredentialsByUserId(userId); if (!currentCredentials) { await this.credentialsRepository.create({ user_id: userId, type: CredentialsType.PASSWORD, secret_data: hashedPassword, - }) + }); } else { await this.credentialsRepository.update(currentCredentials.id, { secret_data: hashedPassword, - }) + }); } } async verifyPassword(userId: string, data: { password: string }) { - const user = await this.usersRepository.findOneById(userId) + const user = await this.usersRepository.findOneById(userId); if (!user) { - throw new Error('User not found') + throw new Error('User not found'); } - const credential = await this.credentialsRepository.findOneByUserIdAndType(userId, CredentialsType.PASSWORD) + const credential = await this.credentialsRepository.findOneByUserIdAndType(userId, CredentialsType.PASSWORD); if (!credential) { - throw new Error('Password credentials not found') + throw new Error('Password credentials not found'); } - const { password } = data - return this.tokenService.verifyHashedToken(credential.secret_data, password) + const { password } = data; + return this.tokenService.verifyHashedToken(credential.secret_data, password); } } diff --git a/src/lib/server/api/tests/iam.service.test.ts b/src/lib/server/api/tests/iam.service.test.ts index 40b0bae..54fab62 100644 --- a/src/lib/server/api/tests/iam.service.test.ts +++ b/src/lib/server/api/tests/iam.service.test.ts @@ -1,32 +1,32 @@ -import 'reflect-metadata' -import { IamService } from '$lib/server/api/services/iam.service' -import { LuciaService } from '$lib/server/api/services/lucia.service' -import { UsersService } from '$lib/server/api/services/users.service' -import { faker } from '@faker-js/faker' -import { container } from 'tsyringe' -import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest' +import 'reflect-metadata'; +import { IamService } from '$lib/server/api/services/iam.service'; +import { SessionsService } from '$lib/server/api/services/sessions.service'; +import { UsersService } from '$lib/server/api/services/users.service'; +import { faker } from '@faker-js/faker'; +import { container } from 'tsyringe'; +import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'; describe('IamService', () => { - let service: IamService - const luciaService = vi.mocked(LuciaService.prototype) - const userService = vi.mocked(UsersService.prototype) + let service: IamService; + const luciaService = vi.mocked(SessionsService.prototype); + const userService = vi.mocked(UsersService.prototype); beforeAll(() => { service = container - .register(LuciaService, { useValue: luciaService }) + .register(SessionsService, { useValue: luciaService }) .register(UsersService, { useValue: userService }) - .resolve(IamService) - }) + .resolve(IamService); + }); beforeEach(() => { - vi.resetAllMocks() - }) + vi.resetAllMocks(); + }); afterAll(() => { - vi.resetAllMocks() - }) + vi.resetAllMocks(); + }); - const timeStampDate = new Date() + const timeStampDate = new Date(); const dbUser = { id: faker.string.uuid(), cuid: 'ciglo1j8q0000t9j4xq8d6p5e', @@ -40,85 +40,85 @@ describe('IamService', () => { theme: 'system', createdAt: timeStampDate, updatedAt: timeStampDate, - } + }; describe('Update Profile', () => { it('should update user', async () => { - userService.findOneById = vi.fn().mockResolvedValueOnce(dbUser) - userService.findOneByUsername = vi.fn().mockResolvedValue(undefined) - userService.updateUser = vi.fn().mockResolvedValue(dbUser) + userService.findOneById = vi.fn().mockResolvedValueOnce(dbUser); + userService.findOneByUsername = vi.fn().mockResolvedValue(undefined); + userService.updateUser = vi.fn().mockResolvedValue(dbUser); - const spy_userService_findOneById = vi.spyOn(userService, 'findOneById') - const spy_userService_findOneByUsername = vi.spyOn(userService, 'findOneByUsername') - const spy_userService_updateUser = vi.spyOn(userService, 'updateUser') + const spy_userService_findOneById = vi.spyOn(userService, 'findOneById'); + const spy_userService_findOneByUsername = vi.spyOn(userService, 'findOneByUsername'); + const spy_userService_updateUser = vi.spyOn(userService, 'updateUser'); await expect( service.updateProfile(faker.string.uuid(), { username: faker.internet.userName(), }), - ).resolves.toEqual(dbUser) - expect(spy_userService_findOneById).toBeCalledTimes(1) - expect(spy_userService_findOneByUsername).toBeCalledTimes(1) - expect(spy_userService_updateUser).toBeCalledTimes(1) - }) + ).resolves.toEqual(dbUser); + expect(spy_userService_findOneById).toBeCalledTimes(1); + expect(spy_userService_findOneByUsername).toBeCalledTimes(1); + expect(spy_userService_updateUser).toBeCalledTimes(1); + }); it('should error on no user found', async () => { - userService.findOneById = vi.fn().mockResolvedValueOnce(undefined) + userService.findOneById = vi.fn().mockResolvedValueOnce(undefined); - const spy_userService_findOneById = vi.spyOn(userService, 'findOneById') - const spy_userService_findOneByUsername = vi.spyOn(userService, 'findOneByUsername') - const spy_userService_updateUser = vi.spyOn(userService, 'updateUser') + const spy_userService_findOneById = vi.spyOn(userService, 'findOneById'); + const spy_userService_findOneByUsername = vi.spyOn(userService, 'findOneByUsername'); + const spy_userService_updateUser = vi.spyOn(userService, 'updateUser'); await expect( service.updateProfile(faker.string.uuid(), { username: faker.internet.userName(), }), ).resolves.toEqual({ error: 'User not found', - }) - expect(spy_userService_findOneById).toBeCalledTimes(1) - expect(spy_userService_findOneByUsername).toBeCalledTimes(0) - expect(spy_userService_updateUser).toBeCalledTimes(0) - }) + }); + expect(spy_userService_findOneById).toBeCalledTimes(1); + expect(spy_userService_findOneByUsername).toBeCalledTimes(0); + expect(spy_userService_updateUser).toBeCalledTimes(0); + }); it('should error on duplicate username', async () => { - userService.findOneById = vi.fn().mockResolvedValueOnce(dbUser) + userService.findOneById = vi.fn().mockResolvedValueOnce(dbUser); userService.findOneByUsername = vi.fn().mockResolvedValue({ id: faker.string.uuid(), - }) - userService.updateUser = vi.fn().mockResolvedValue(dbUser) + }); + userService.updateUser = vi.fn().mockResolvedValue(dbUser); - const spy_userService_findOneById = vi.spyOn(userService, 'findOneById') - const spy_userService_findOneByUsername = vi.spyOn(userService, 'findOneByUsername') - const spy_userService_updateUser = vi.spyOn(userService, 'updateUser') + const spy_userService_findOneById = vi.spyOn(userService, 'findOneById'); + const spy_userService_findOneByUsername = vi.spyOn(userService, 'findOneByUsername'); + const spy_userService_updateUser = vi.spyOn(userService, 'updateUser'); await expect( service.updateProfile(faker.string.uuid(), { username: faker.internet.userName(), }), ).resolves.toEqual({ error: 'Username already in use', - }) - expect(spy_userService_findOneById).toBeCalledTimes(1) - expect(spy_userService_findOneByUsername).toBeCalledTimes(1) - expect(spy_userService_updateUser).toBeCalledTimes(0) - }) - }) + }); + expect(spy_userService_findOneById).toBeCalledTimes(1); + expect(spy_userService_findOneByUsername).toBeCalledTimes(1); + expect(spy_userService_updateUser).toBeCalledTimes(0); + }); + }); it('should not error if the user id of new username is the current user id', async () => { - userService.findOneById = vi.fn().mockResolvedValueOnce(dbUser) + userService.findOneById = vi.fn().mockResolvedValueOnce(dbUser); userService.findOneByUsername = vi.fn().mockResolvedValue({ id: dbUser.id, - }) - userService.updateUser = vi.fn().mockResolvedValue(dbUser) + }); + userService.updateUser = vi.fn().mockResolvedValue(dbUser); - const spy_userService_findOneById = vi.spyOn(userService, 'findOneById') - const spy_userService_findOneByUsername = vi.spyOn(userService, 'findOneByUsername') - const spy_userService_updateUser = vi.spyOn(userService, 'updateUser') + const spy_userService_findOneById = vi.spyOn(userService, 'findOneById'); + const spy_userService_findOneByUsername = vi.spyOn(userService, 'findOneByUsername'); + const spy_userService_updateUser = vi.spyOn(userService, 'updateUser'); await expect( service.updateProfile(dbUser.id, { username: dbUser.id, }), - ).resolves.toEqual(dbUser) - expect(spy_userService_findOneById).toBeCalledTimes(1) - expect(spy_userService_findOneByUsername).toBeCalledTimes(1) - expect(spy_userService_updateUser).toBeCalledTimes(1) - }) -}) + ).resolves.toEqual(dbUser); + expect(spy_userService_findOneById).toBeCalledTimes(1); + expect(spy_userService_findOneByUsername).toBeCalledTimes(1); + expect(spy_userService_updateUser).toBeCalledTimes(1); + }); +}); diff --git a/src/lib/server/api/tests/user_roles.service.test.ts b/src/lib/server/api/tests/user_roles.service.test.ts index 89cde2a..b1ba2f6 100644 --- a/src/lib/server/api/tests/user_roles.service.test.ts +++ b/src/lib/server/api/tests/user_roles.service.test.ts @@ -1,38 +1,38 @@ -import 'reflect-metadata' -import { RoleName } from '$lib/server/api/databases/tables' -import { faker } from '@faker-js/faker' -import { container } from 'tsyringe' -import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest' -import { UserRolesRepository } from '../repositories/user_roles.repository' -import { RolesService } from '../services/roles.service' -import { UserRolesService } from '../services/user_roles.service' +import 'reflect-metadata'; +import { faker } from '@faker-js/faker'; +import { container } from 'tsyringe'; +import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest'; +import { RoleName } from '../databases/postgres/tables'; +import { UserRolesRepository } from '../repositories/user_roles.repository'; +import { RolesService } from '../services/roles.service'; +import { UserRolesService } from '../services/user_roles.service'; describe('UserRolesService', () => { - let service: UserRolesService - const userRolesRepository = vi.mocked(UserRolesRepository.prototype) - const rolesService = vi.mocked(RolesService.prototype) + let service: UserRolesService; + const userRolesRepository = vi.mocked(UserRolesRepository.prototype); + const rolesService = vi.mocked(RolesService.prototype); beforeAll(() => { service = container .register(UserRolesRepository, { useValue: userRolesRepository }) .register(RolesService, { useValue: rolesService }) - .resolve(UserRolesService) - }) + .resolve(UserRolesService); + }); afterAll(() => { - vi.resetAllMocks() - }) + vi.resetAllMocks(); + }); - const timeStampDate = new Date() - const roleUUID = faker.string.uuid() - const userUUID = faker.string.uuid() + const timeStampDate = new Date(); + const roleUUID = faker.string.uuid(); + const userUUID = faker.string.uuid(); const dbRole = { id: roleUUID, cuid: 'ciglo1j8q0000t9j4xq8d6p5e', name: RoleName.ADMIN, createdAt: timeStampDate, updatedAt: timeStampDate, - } + }; const dbUserRole = { id: faker.string.uuid(), @@ -42,34 +42,34 @@ describe('UserRolesService', () => { primary: true, createdAt: timeStampDate, updatedAt: timeStampDate, - } + }; describe('Create User Role', () => { it('should resolve', async () => { - rolesService.findOneByNameOrThrow = vi.fn().mockResolvedValue(dbRole satisfies Awaited>) + rolesService.findOneByNameOrThrow = vi.fn().mockResolvedValue(dbRole satisfies Awaited>); - userRolesRepository.create = vi.fn().mockResolvedValue(dbUserRole satisfies Awaited>) + userRolesRepository.create = vi.fn().mockResolvedValue(dbUserRole satisfies Awaited>); - const spy_rolesService_findOneByNameOrThrow = vi.spyOn(rolesService, 'findOneByNameOrThrow') - const spy_userRolesRepository_create = vi.spyOn(userRolesRepository, 'create') + const spy_rolesService_findOneByNameOrThrow = vi.spyOn(rolesService, 'findOneByNameOrThrow'); + const spy_userRolesRepository_create = vi.spyOn(userRolesRepository, 'create'); - await expect(service.addRoleToUser(userUUID, RoleName.ADMIN, true)).resolves.not.toThrowError() - expect(spy_rolesService_findOneByNameOrThrow).toBeCalledWith(RoleName.ADMIN) - expect(spy_rolesService_findOneByNameOrThrow).toBeCalledTimes(1) + await expect(service.addRoleToUser(userUUID, RoleName.ADMIN, true)).resolves.not.toThrowError(); + expect(spy_rolesService_findOneByNameOrThrow).toBeCalledWith(RoleName.ADMIN); + expect(spy_rolesService_findOneByNameOrThrow).toBeCalledTimes(1); expect(spy_userRolesRepository_create).toBeCalledWith({ user_id: userUUID, role_id: dbRole.id, primary: true, - }) - expect(spy_userRolesRepository_create).toBeCalledTimes(1) - }) + }); + expect(spy_userRolesRepository_create).toBeCalledTimes(1); + }); it('should error on no role found', async () => { - rolesService.findOneByNameOrThrow = vi.fn().mockResolvedValue(undefined) + rolesService.findOneByNameOrThrow = vi.fn().mockResolvedValue(undefined); - const spy_rolesService_findOneByNameOrThrow = vi.spyOn(rolesService, 'findOneByNameOrThrow') - await expect(service.addRoleToUser(userUUID, RoleName.ADMIN, true)).rejects.toThrowError(`Role with name ${RoleName.ADMIN} not found`) - expect(spy_rolesService_findOneByNameOrThrow).toBeCalledWith(RoleName.ADMIN) - expect(spy_rolesService_findOneByNameOrThrow).toBeCalledTimes(1) - }) - }) -}) + const spy_rolesService_findOneByNameOrThrow = vi.spyOn(rolesService, 'findOneByNameOrThrow'); + await expect(service.addRoleToUser(userUUID, RoleName.ADMIN, true)).rejects.toThrowError(`Role with name ${RoleName.ADMIN} not found`); + expect(spy_rolesService_findOneByNameOrThrow).toBeCalledWith(RoleName.ADMIN); + expect(spy_rolesService_findOneByNameOrThrow).toBeCalledTimes(1); + }); + }); +}); diff --git a/src/lib/server/api/tests/users.service.test.ts b/src/lib/server/api/tests/users.service.test.ts index 8757df8..b080399 100644 --- a/src/lib/server/api/tests/users.service.test.ts +++ b/src/lib/server/api/tests/users.service.test.ts @@ -1,26 +1,26 @@ -import 'reflect-metadata' -import { CredentialsType } from '$lib/server/api/databases/tables' -import { faker } from '@faker-js/faker' -import { container } from 'tsyringe' -import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest' -import { CredentialsRepository } from '../repositories/credentials.repository' -import { UsersRepository } from '../repositories/users.repository' -import { CollectionsService } from '../services/collections.service' -import { DrizzleService } from '../services/drizzle.service' -import { TokensService } from '../services/tokens.service' -import { UserRolesService } from '../services/user_roles.service' -import { UsersService } from '../services/users.service' -import { WishlistsService } from '../services/wishlists.service' +import 'reflect-metadata'; +import { faker } from '@faker-js/faker'; +import { container } from 'tsyringe'; +import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest'; +import { CredentialsType } from '../databases/postgres/tables'; +import { CredentialsRepository } from '../repositories/credentials.repository'; +import { UsersRepository } from '../repositories/users.repository'; +import { CollectionsService } from '../services/collections.service'; +import { DrizzleService } from '../services/drizzle.service'; +import { TokensService } from '../services/tokens.service'; +import { UserRolesService } from '../services/user_roles.service'; +import { UsersService } from '../services/users.service'; +import { WishlistsService } from '../services/wishlists.service'; describe('UsersService', () => { - let service: UsersService - const credentialsRepository = vi.mocked(CredentialsRepository.prototype) - const drizzleService = vi.mocked(DrizzleService.prototype, { deep: true }) - const tokensService = vi.mocked(TokensService.prototype) - const usersRepository = vi.mocked(UsersRepository.prototype) - const userRolesService = vi.mocked(UserRolesService.prototype) - const wishlistsService = vi.mocked(WishlistsService.prototype) - const collectionsService = vi.mocked(CollectionsService.prototype) + let service: UsersService; + const credentialsRepository = vi.mocked(CredentialsRepository.prototype); + const drizzleService = vi.mocked(DrizzleService.prototype, { deep: true }); + const tokensService = vi.mocked(TokensService.prototype); + const usersRepository = vi.mocked(UsersRepository.prototype); + const userRolesService = vi.mocked(UserRolesService.prototype); + const wishlistsService = vi.mocked(WishlistsService.prototype); + const collectionsService = vi.mocked(CollectionsService.prototype); // Mocking the dependencies vi.mock('pg', () => ({ @@ -28,14 +28,14 @@ describe('UsersService', () => { connect: vi.fn(), end: vi.fn(), })), - })) + })); vi.mock('drizzle-orm/node-postgres', () => ({ drizzle: vi.fn().mockImplementation(() => ({ transaction: vi.fn().mockImplementation((callback) => callback()), // Add other methods you need to mock })), - })) + })); beforeAll(() => { service = container @@ -46,20 +46,20 @@ describe('UsersService', () => { .register(UserRolesService, { useValue: userRolesService }) .register(WishlistsService, { useValue: wishlistsService }) .register(CollectionsService, { useValue: collectionsService }) - .resolve(UsersService) + .resolve(UsersService); drizzleService.db = { transaction: vi.fn().mockImplementation(async (callback) => { - return await callback() + return await callback(); }), - } as any - }) + } as any; + }); afterAll(() => { - vi.resetAllMocks() - }) + vi.resetAllMocks(); + }); - const timeStampDate = new Date() + const timeStampDate = new Date(); const dbUser = { id: faker.string.uuid(), cuid: 'ciglo1j8q0000t9j4xq8d6p5e', @@ -73,7 +73,7 @@ describe('UsersService', () => { theme: 'system', createdAt: timeStampDate, updatedAt: timeStampDate, - } + }; const dbCredentials = { id: faker.string.uuid(), user_id: dbUser.id, @@ -81,27 +81,27 @@ describe('UsersService', () => { secret_data: 'hashedPassword', createdAt: timeStampDate, updatedAt: timeStampDate, - } + }; describe('Create User', () => { it('should resolve', async () => { - const hashedPassword = 'testhash' - tokensService.createHashedToken = vi.fn().mockResolvedValue(hashedPassword) + const hashedPassword = 'testhash'; + tokensService.createHashedToken = vi.fn().mockResolvedValue(hashedPassword); // drizzleService.db = { // transaction: vi.fn().mockResolvedValue(dbUser satisfies Awaited>), // } - usersRepository.create = vi.fn().mockResolvedValue(dbUser satisfies Awaited>) - credentialsRepository.create = vi.fn().mockResolvedValue(dbCredentials satisfies Awaited>) - userRolesService.addRoleToUser = vi.fn().mockResolvedValue(undefined) - wishlistsService.createEmptyNoName = vi.fn().mockResolvedValue(undefined) - collectionsService.createEmptyNoName = vi.fn().mockResolvedValue(undefined) + usersRepository.create = vi.fn().mockResolvedValue(dbUser satisfies Awaited>); + credentialsRepository.create = vi.fn().mockResolvedValue(dbCredentials satisfies Awaited>); + userRolesService.addRoleToUser = vi.fn().mockResolvedValue(undefined); + wishlistsService.createEmptyNoName = vi.fn().mockResolvedValue(undefined); + collectionsService.createEmptyNoName = vi.fn().mockResolvedValue(undefined); - const spy_tokensService_createHashToken = vi.spyOn(tokensService, 'createHashedToken') - const spy_usersRepository_create = vi.spyOn(usersRepository, 'create') - const spy_credentialsRepository_create = vi.spyOn(credentialsRepository, 'create') - const spy_userRolesService_addRoleToUser = vi.spyOn(userRolesService, 'addRoleToUser') - const spy_wishlistsService_createEmptyNoName = vi.spyOn(wishlistsService, 'createEmptyNoName') - const spy_collectionsService_createEmptyNoName = vi.spyOn(collectionsService, 'createEmptyNoName') + const spy_tokensService_createHashToken = vi.spyOn(tokensService, 'createHashedToken'); + const spy_usersRepository_create = vi.spyOn(usersRepository, 'create'); + const spy_credentialsRepository_create = vi.spyOn(credentialsRepository, 'create'); + const spy_userRolesService_addRoleToUser = vi.spyOn(userRolesService, 'addRoleToUser'); + const spy_wishlistsService_createEmptyNoName = vi.spyOn(wishlistsService, 'createEmptyNoName'); + const spy_collectionsService_createEmptyNoName = vi.spyOn(collectionsService, 'createEmptyNoName'); await expect( service.create({ firstName: faker.person.firstName(), @@ -111,46 +111,46 @@ describe('UsersService', () => { password: faker.string.alphanumeric(10), confirm_password: faker.string.alphanumeric(10), }), - ).resolves.toEqual(dbUser) - expect(spy_tokensService_createHashToken).toBeCalledTimes(1) - expect(spy_usersRepository_create).toBeCalledTimes(1) - expect(spy_credentialsRepository_create).toBeCalledTimes(1) - expect(spy_userRolesService_addRoleToUser).toBeCalledTimes(1) - expect(spy_wishlistsService_createEmptyNoName).toBeCalledTimes(1) - expect(spy_collectionsService_createEmptyNoName).toBeCalledTimes(1) - }) - }) + ).resolves.toEqual(dbUser); + expect(spy_tokensService_createHashToken).toBeCalledTimes(1); + expect(spy_usersRepository_create).toBeCalledTimes(1); + expect(spy_credentialsRepository_create).toBeCalledTimes(1); + expect(spy_userRolesService_addRoleToUser).toBeCalledTimes(1); + expect(spy_wishlistsService_createEmptyNoName).toBeCalledTimes(1); + expect(spy_collectionsService_createEmptyNoName).toBeCalledTimes(1); + }); + }); describe('Update User', () => { it('should resolve Password Exiting Credentials', async () => { - const hashedPassword = 'testhash' - tokensService.createHashedToken = vi.fn().mockResolvedValue(hashedPassword) - credentialsRepository.update = vi.fn().mockResolvedValue(dbCredentials satisfies Awaited>) + const hashedPassword = 'testhash'; + tokensService.createHashedToken = vi.fn().mockResolvedValue(hashedPassword); + credentialsRepository.update = vi.fn().mockResolvedValue(dbCredentials satisfies Awaited>); credentialsRepository.findPasswordCredentialsByUserId = vi .fn() - .mockResolvedValue(dbCredentials satisfies Awaited>) + .mockResolvedValue(dbCredentials satisfies Awaited>); - const spy_tokensService_createHashToken = vi.spyOn(tokensService, 'createHashedToken') - const spy_credentialsRepository_findPasswordCredentialsByUserId = vi.spyOn(credentialsRepository, 'findPasswordCredentialsByUserId') - const spy_credentialsRepository_update = vi.spyOn(credentialsRepository, 'update') - await expect(service.updatePassword(dbUser.id, faker.string.alphanumeric(10))).resolves.toBeUndefined() - expect(spy_tokensService_createHashToken).toBeCalledTimes(1) - expect(spy_credentialsRepository_findPasswordCredentialsByUserId).toBeCalledTimes(1) - expect(spy_credentialsRepository_update).toBeCalledTimes(1) - }) + const spy_tokensService_createHashToken = vi.spyOn(tokensService, 'createHashedToken'); + const spy_credentialsRepository_findPasswordCredentialsByUserId = vi.spyOn(credentialsRepository, 'findPasswordCredentialsByUserId'); + const spy_credentialsRepository_update = vi.spyOn(credentialsRepository, 'update'); + await expect(service.updatePassword(dbUser.id, faker.string.alphanumeric(10))).resolves.toBeUndefined(); + expect(spy_tokensService_createHashToken).toBeCalledTimes(1); + expect(spy_credentialsRepository_findPasswordCredentialsByUserId).toBeCalledTimes(1); + expect(spy_credentialsRepository_update).toBeCalledTimes(1); + }); it('Should Create User Password No Existing Credentials', async () => { - const hashedPassword = 'testhash' - tokensService.createHashedToken = vi.fn().mockResolvedValue(hashedPassword) - credentialsRepository.findPasswordCredentialsByUserId = vi.fn().mockResolvedValue(null) - credentialsRepository.create = vi.fn().mockResolvedValue(dbCredentials satisfies Awaited>) + const hashedPassword = 'testhash'; + tokensService.createHashedToken = vi.fn().mockResolvedValue(hashedPassword); + credentialsRepository.findPasswordCredentialsByUserId = vi.fn().mockResolvedValue(null); + credentialsRepository.create = vi.fn().mockResolvedValue(dbCredentials satisfies Awaited>); - const spy_tokensService_createHashToken = vi.spyOn(tokensService, 'createHashedToken') - const spy_credentialsRepository_create = vi.spyOn(credentialsRepository, 'create') - const spy_credentialsRepository_findPasswordCredentialsByUserId = vi.spyOn(credentialsRepository, 'findPasswordCredentialsByUserId') + const spy_tokensService_createHashToken = vi.spyOn(tokensService, 'createHashedToken'); + const spy_credentialsRepository_create = vi.spyOn(credentialsRepository, 'create'); + const spy_credentialsRepository_findPasswordCredentialsByUserId = vi.spyOn(credentialsRepository, 'findPasswordCredentialsByUserId'); - await expect(service.updatePassword(dbUser.id, faker.string.alphanumeric(10))).resolves.not.toThrow() - expect(spy_tokensService_createHashToken).toBeCalledTimes(1) - expect(spy_credentialsRepository_findPasswordCredentialsByUserId).toBeCalledTimes(1) - expect(spy_credentialsRepository_create).toHaveBeenCalledTimes(1) - }) - }) -}) + await expect(service.updatePassword(dbUser.id, faker.string.alphanumeric(10))).resolves.not.toThrow(); + expect(spy_tokensService_createHashToken).toBeCalledTimes(1); + expect(spy_credentialsRepository_findPasswordCredentialsByUserId).toBeCalledTimes(1); + expect(spy_credentialsRepository_create).toHaveBeenCalledTimes(1); + }); + }); +}); diff --git a/src/lib/server/auth-utils.ts b/src/lib/server/auth-utils.ts index d0d6693..3d163b2 100644 --- a/src/lib/server/auth-utils.ts +++ b/src/lib/server/auth-utils.ts @@ -1,19 +1,19 @@ -import { eq } from 'drizzle-orm' -import { type Session, type User, generateIdFromEntropySize } from 'lucia' -import { TimeSpan, createDate } from 'oslo' -import { password_reset_tokens } from './api/databases/tables' -import { db } from './api/packages/drizzle' +import { eq } from 'drizzle-orm'; +import { type Session, type User, generateIdFromEntropySize } from 'lucia'; +import { TimeSpan, createDate } from 'oslo'; +import { password_reset_tokens } from './api/databases/postgres/tables'; +import { db } from './api/packages/drizzle'; export async function createPasswordResetToken(userId: string): Promise { // optionally invalidate all existing tokens - await db.delete(password_reset_tokens).where(eq(password_reset_tokens.user_id, userId)) - const tokenId = generateIdFromEntropySize(40) + await db.delete(password_reset_tokens).where(eq(password_reset_tokens.user_id, userId)); + const tokenId = generateIdFromEntropySize(40); await db.insert(password_reset_tokens).values({ id: tokenId, user_id: userId, expires_at: createDate(new TimeSpan(2, 'h')), - }) - return tokenId + }); + return tokenId; } /** @@ -24,7 +24,7 @@ export async function createPasswordResetToken(userId: string): Promise * @returns True if the user is not fully authenticated, otherwise false. */ export function userNotFullyAuthenticated(user: User | null, session: Session | null) { - return user && session && session.isTwoFactorAuthEnabled && !session.isTwoFactorAuthenticated + return user && session && session.isTwoFactorAuthEnabled && !session.isTwoFactorAuthenticated; } /** @@ -35,7 +35,7 @@ export function userNotFullyAuthenticated(user: User | null, session: Session | * @returns {boolean} True if the user is not fully authenticated, otherwise false. */ export function userNotAuthenticated(user: User | null, session: Session | null) { - return !user || !session || userNotFullyAuthenticated(user, session) + return !user || !session || userNotFullyAuthenticated(user, session); } /** @@ -46,5 +46,5 @@ export function userNotAuthenticated(user: User | null, session: Session | null) * @returns {boolean} True if the user is fully authenticated, otherwise false. */ export function userFullyAuthenticated(user: User | null, session: Session | null) { - return !userNotAuthenticated(user, session) + return !userNotAuthenticated(user, session); } diff --git a/src/lib/types.ts b/src/lib/types.ts index 5c859d3..8d9a041 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -1,36 +1,36 @@ -import type { collections } from '$lib/server/api/databases/tables' -import type { SvelteComponent } from 'svelte' +import type { collections } from 'server/api/databases/postgres/tables'; +import type { SvelteComponent } from 'svelte'; -export type Message = { status: 'error' | 'success' | 'warning' | 'info'; text: string } +export type Message = { status: 'error' | 'success' | 'warning' | 'info'; text: string }; export type Route = { - href: string - label: string -} + href: string; + label: string; +}; export type Dialog = { - isOpen: boolean - content?: typeof SvelteComponent - additionalData?: SavedGameType | GameType -} + isOpen: boolean; + content?: typeof SvelteComponent; + additionalData?: SavedGameType | GameType; +}; export type Search = { - name: string - minAge: string - minPlayers: string - maxPlayers: string - exactMinAge: string - exactMinPlayers: string - exactMaxPlayers: string - skip: number - currentPage: number - limit: number -} + name: string; + minAge: string; + minPlayers: string; + maxPlayers: string; + exactMinAge: string; + exactMinPlayers: string; + exactMaxPlayers: string; + skip: number; + currentPage: number; + limit: number; +}; export type BoredStore = { - loading: boolean - dialog: Dialog -} + loading: boolean; + dialog: Dialog; +}; export enum ToastType { INFO = 'INFO', @@ -39,141 +39,141 @@ export enum ToastType { } export type ToastData = { - id: number - duration: number - dismissible: boolean - showButton: boolean - autoDismiss: boolean - type: ToastType - message: string -} + id: number; + duration: number; + dismissible: boolean; + showButton: boolean; + autoDismiss: boolean; + type: ToastType; + message: string; +}; export type GameMechanic = { - id: string - name: string - boardGameAtlasLink: string -} + id: string; + name: string; + boardGameAtlasLink: string; +}; export type SavedGameType = { - id: string - name: string - thumb_url: string - players: string - playtime: string - mechanics: GameMechanic[] - searchTerms: string - includeInRandom: boolean -} + id: string; + name: string; + thumb_url: string; + players: string; + playtime: string; + mechanics: GameMechanic[]; + searchTerms: string; + includeInRandom: boolean; +}; export type ListGameType = { - id: string - game_id: string - collection_id: string | undefined - wishlist_id: string | undefined - times_played: number - thumb_url: string -} + id: string; + game_id: string; + collection_id: string | undefined; + wishlist_id: string | undefined; + times_played: number; + thumb_url: string; +}; export type MechanicType = { - id: string -} + id: string; +}; export type CategoryType = { - id: string -} + id: string; +}; export type PublisherType = { - id: string -} + id: string; +}; export type DesignerType = { - id: string -} + id: string; +}; export type ArtistType = { - id: string -} + id: string; +}; export type ExpansionType = { - id: string -} + id: string; +}; -export type BGGLinkType = 'boardgamecategory' | 'boardgamemechanic' | 'boardgameexpansion' | 'boardgameartist' | 'boardgamepublisher' +export type BGGLinkType = 'boardgamecategory' | 'boardgamemechanic' | 'boardgameexpansion' | 'boardgameartist' | 'boardgamepublisher'; export type BGGLink = { - id: number - type: BGGLinkType - value: string -} + id: number; + type: BGGLinkType; + value: string; +}; export type GameType = { - id: string - name: string - slug: string - url: string - edit_url: string - thumb_url: string - image_url: string - price: number - price_ca: number - price_uk: number - price_au: number - msrp: number - year_published: number - categories: CategoryType[] - mechanics: MechanicType[] - primary_publisher: PublisherType - publishers: PublisherType[] - primary_designer: DesignerType - designers: DesignerType[] - developers: string[] - artists: ArtistType[] - expansions: ExpansionType[] - min_players: number - max_players: number - min_playtime: number - max_playtime: number - min_age: number - description: string - players: string - playtime: number - external_id: number -} + id: string; + name: string; + slug: string; + url: string; + edit_url: string; + thumb_url: string; + image_url: string; + price: number; + price_ca: number; + price_uk: number; + price_au: number; + msrp: number; + year_published: number; + categories: CategoryType[]; + mechanics: MechanicType[]; + primary_publisher: PublisherType; + publishers: PublisherType[]; + primary_designer: DesignerType; + designers: DesignerType[]; + developers: string[]; + artists: ArtistType[]; + expansions: ExpansionType[]; + min_players: number; + max_players: number; + min_playtime: number; + max_playtime: number; + min_age: number; + description: string; + players: string; + playtime: number; + external_id: number; +}; export type SearchQuery = { - limit?: number - skip?: number - ids?: string[] - list_id?: string - random?: boolean - q?: string - exact?: boolean - designer?: string - publisher?: string - artist?: string - mechanics?: string - categories?: string - order_by?: string - ascending?: boolean - min_players?: number - max_players?: number - min_playtime?: number - max_playtime?: number - min_age?: number - year_published?: number - gt_min_players?: number - gt_max_players?: number - gt_min_playtime?: number - gt_max_playtime?: number - gt_min_age?: number - gt_year_published?: number - lt_min_players?: number - lt_max_players?: number - lt_min_playtime?: number - lt_max_playtime?: number - lt_min_age?: number - lt_year_published?: number - fields?: string -} + limit?: number; + skip?: number; + ids?: string[]; + list_id?: string; + random?: boolean; + q?: string; + exact?: boolean; + designer?: string; + publisher?: string; + artist?: string; + mechanics?: string; + categories?: string; + order_by?: string; + ascending?: boolean; + min_players?: number; + max_players?: number; + min_playtime?: number; + max_playtime?: number; + min_age?: number; + year_published?: number; + gt_min_players?: number; + gt_max_players?: number; + gt_min_playtime?: number; + gt_max_playtime?: number; + gt_min_age?: number; + gt_year_published?: number; + lt_min_players?: number; + lt_max_players?: number; + lt_min_playtime?: number; + lt_max_playtime?: number; + lt_min_age?: number; + lt_year_published?: number; + fields?: string; +}; -export type UICollection = Pick +export type UICollection = Pick; diff --git a/src/lib/utils/db/categoryUtils.ts b/src/lib/utils/db/categoryUtils.ts index 392d267..8ba4381 100644 --- a/src/lib/utils/db/categoryUtils.ts +++ b/src/lib/utils/db/categoryUtils.ts @@ -1,19 +1,25 @@ -import { PUBLIC_SITE_URL } from '$env/static/public' -import { type Categories, type Mechanics, categoriesTable, categoriesToExternalIdsTable, externalIdsTable } from '$lib/server/api/databases/tables' -import { db } from '$lib/server/api/packages/drizzle' -import { error } from '@sveltejs/kit' -import { eq } from 'drizzle-orm' -import kebabCase from 'just-kebab-case' +import { PUBLIC_SITE_URL } from '$env/static/public'; +import { db } from '$lib/server/api/packages/drizzle'; +import { error } from '@sveltejs/kit'; +import { eq } from 'drizzle-orm'; +import kebabCase from 'just-kebab-case'; +import { + type Categories, + type Mechanics, + categoriesTable, + categoriesToExternalIdsTable, + externalIdsTable, +} from '../../server/api/databases/postgres/tables'; export async function createCategory(locals: App.Locals, category: Categories, externalId: string) { if (!category || !externalId || externalId === '') { - error(400, 'Invalid Request') + error(400, 'Invalid Request'); } try { const dbExternalId = await db.query.externalIds.findFirst({ where: eq(externalIdsTable.externalId, externalId), - }) + }); if (dbExternalId) { const foundCategory = await db @@ -23,22 +29,22 @@ export async function createCategory(locals: App.Locals, category: Categories, e slug: categoriesTable.slug, }) .from(categoriesTable) - .leftJoin(categoriesToExternalIdsTable, eq(categoriesToExternalIdsTable.externalId, externalId)) - console.log('Mechanic already exists', foundCategory) + .leftJoin(categoriesToExternalIdsTable, eq(categoriesToExternalIdsTable.externalId, externalId)); + console.log('Mechanic already exists', foundCategory); if (foundCategory.length > 0) { - console.log('Mechanic name', foundCategory[0].name) + console.log('Mechanic name', foundCategory[0].name); return new Response('Mechanic already exists', { headers: { 'Content-Type': 'application/json', Location: `${PUBLIC_SITE_URL}/api/mechanic/${foundCategory[0].id}`, }, status: 409, - }) + }); } } - let dbCategory: Mechanics[] = [] - console.log('Creating category', JSON.stringify(category, null, 2)) + let dbCategory: Mechanics[] = []; + console.log('Creating category', JSON.stringify(category, null, 2)); await db.transaction(async (transaction) => { dbCategory = await transaction .insert(categoriesTable) @@ -46,32 +52,32 @@ export async function createCategory(locals: App.Locals, category: Categories, e name: category.name, slug: kebabCase(category.name ?? category.slug ?? ''), }) - .returning() + .returning(); const dbExternalIds = await transaction .insert(externalIdsTable) .values({ externalId, type: 'category', }) - .returning({ id: externalIdsTable.id }) + .returning({ id: externalIdsTable.id }); await transaction.insert(categoriesToExternalIdsTable).values({ categoryId: dbCategory[0].id, externalId: dbExternalIds[0].id, - }) - }) + }); + }); if (dbCategory.length === 0) { return new Response('Could not create category', { status: 500, - }) + }); } - console.log('Created category', JSON.stringify(dbCategory[0], null, 2)) + console.log('Created category', JSON.stringify(dbCategory[0], null, 2)); return new Response(JSON.stringify(dbCategory[0]), { status: 201, - }) + }); } catch (e) { - console.error(e) - throw new Error('Something went wrong creating Category') + console.error(e); + throw new Error('Something went wrong creating Category'); } } diff --git a/src/lib/utils/db/expansionUtils.ts b/src/lib/utils/db/expansionUtils.ts index 07bea23..f26b5bd 100644 --- a/src/lib/utils/db/expansionUtils.ts +++ b/src/lib/utils/db/expansionUtils.ts @@ -1,12 +1,12 @@ -import { PUBLIC_SITE_URL } from '$env/static/public' -import { type Expansions, expansionsTable } from '$lib/server/api/databases/tables' -import { db } from '$lib/server/api/packages/drizzle' -import { error } from '@sveltejs/kit' -import { and, eq } from 'drizzle-orm' +import { PUBLIC_SITE_URL } from '$env/static/public'; +import { db } from '$lib/server/api/packages/drizzle'; +import { error } from '@sveltejs/kit'; +import { and, eq } from 'drizzle-orm'; +import { type Expansions, expansionsTable } from '../../server/api/databases/postgres/tables'; export async function createExpansion(locals: App.Locals, expansion: Expansions) { if (!expansion || expansion?.base_game_id === '' || expansion?.game_id === '') { - error(400, 'Invalid Request') + error(400, 'Invalid Request'); } try { @@ -17,41 +17,41 @@ export async function createExpansion(locals: App.Locals, expansion: Expansions) game_id: true, base_game_id: true, }, - }) - console.log('Expansion already exists', foundExpansion) + }); + console.log('Expansion already exists', foundExpansion); if (foundExpansion) { - console.log('Expansion Game ID', foundExpansion.game_id) + console.log('Expansion Game ID', foundExpansion.game_id); return new Response('Expansion already exists', { headers: { 'Content-Type': 'application/json', Location: `${PUBLIC_SITE_URL}/api/game/${foundExpansion.game_id}`, }, status: 409, - }) + }); } - console.log('Creating expansion', JSON.stringify(expansion, null, 2)) + console.log('Creating expansion', JSON.stringify(expansion, null, 2)); const dbExpansion = await db .insert(expansionsTable) .values({ base_game_id: expansion.base_game_id, game_id: expansion.game_id, }) - .returning() + .returning(); if (dbExpansion.length === 0) { return new Response('Could not create expansion', { status: 500, - }) + }); } - console.log('Created expansion', JSON.stringify(dbExpansion[0], null, 2)) + console.log('Created expansion', JSON.stringify(dbExpansion[0], null, 2)); return new Response(JSON.stringify(dbExpansion[0]), { status: 201, - }) + }); } catch (e) { - console.error(e) - throw new Error('Something went wrong creating Expansion') + console.error(e); + throw new Error('Something went wrong creating Expansion'); } } diff --git a/src/lib/utils/db/gameUtils.ts b/src/lib/utils/db/gameUtils.ts index 50e79dc..f9c4d7c 100644 --- a/src/lib/utils/db/gameUtils.ts +++ b/src/lib/utils/db/gameUtils.ts @@ -1,36 +1,36 @@ -import { PUBLIC_SITE_URL } from '$env/static/public' -import { type Games, externalIdsTable, gamesTable, gamesToExternalIdsTable } from '$lib/server/api/databases/tables' -import { db } from '$lib/server/api/packages/drizzle' -import { error } from '@sveltejs/kit' -import { eq } from 'drizzle-orm' -import kebabCase from 'just-kebab-case' +import { PUBLIC_SITE_URL } from '$env/static/public'; +import { db } from '$lib/server/api/packages/drizzle'; +import { error } from '@sveltejs/kit'; +import { eq } from 'drizzle-orm'; +import kebabCase from 'just-kebab-case'; +import { type Games, externalIdsTable, gamesTable, gamesToExternalIdsTable } from '../../server/api/databases/postgres/tables'; export async function getGame(locals: App.Locals, id: string) { if (!id || id === '') { - error(400, 'Invalid Request') + error(400, 'Invalid Request'); } try { return await db.query.games.findFirst({ where: eq(gamesTable.id, id), - }) + }); } catch (e) { - console.error(e) + console.error(e); return new Response('Could not get gamesTable', { status: 500, - }) + }); } } export async function createGame(locals: App.Locals, game: Games, externalId: string) { if (!game || !externalId || externalId === '') { - error(400, 'Invalid Request') + error(400, 'Invalid Request'); } try { const dbExternalId = await db.query.externalIds.findFirst({ where: eq(externalIdsTable.externalId, externalId), - }) + }); if (dbExternalId) { const foundGame = await db @@ -40,22 +40,22 @@ export async function createGame(locals: App.Locals, game: Games, externalId: st slug: gamesTable.slug, }) .from(gamesTable) - .leftJoin(gamesToExternalIdsTable, eq(gamesToExternalIdsTable.externalId, externalId)) - console.log('Game already exists', foundGame) + .leftJoin(gamesToExternalIdsTable, eq(gamesToExternalIdsTable.externalId, externalId)); + console.log('Game already exists', foundGame); if (foundGame.length > 0) { - console.log('Game name', foundGame[0].name) + console.log('Game name', foundGame[0].name); return new Response('Game already exists', { headers: { 'Content-Type': 'application/json', Location: `${PUBLIC_SITE_URL}/api/game/${foundGame[0].id}`, }, status: 409, - }) + }); } } - let dbGames: Games[] = [] - console.log('Creating game', JSON.stringify(game, null, 2)) + let dbGames: Games[] = []; + console.log('Creating game', JSON.stringify(game, null, 2)); await db.transaction(async (transaction) => { dbGames = await transaction .insert(gamesTable) @@ -73,46 +73,46 @@ export async function createGame(locals: App.Locals, game: Games, externalId: st min_playtime: game.min_playtime, max_playtime: game.max_playtime, }) - .returning() + .returning(); const dbExternalIds = await transaction .insert(externalIdsTable) .values({ externalId, type: 'game', }) - .returning({ id: externalIdsTable.id }) + .returning({ id: externalIdsTable.id }); await transaction.insert(gamesToExternalIdsTable).values({ gameId: dbGames[0].id, externalId: dbExternalIds[0].id, - }) - }) + }); + }); if (dbGames.length === 0) { return new Response('Could not create game', { status: 500, - }) + }); } - console.log('Created game', JSON.stringify(dbGames[0], null, 2)) + console.log('Created game', JSON.stringify(dbGames[0], null, 2)); return new Response(JSON.stringify(dbGames[0]), { status: 201, - }) + }); } catch (e) { - console.error(e) - throw new Error('Something went wrong creating Game') + console.error(e); + throw new Error('Something went wrong creating Game'); } } export async function createOrUpdateGameMinimal(locals: App.Locals, game: Games, externalId: string) { if (!game || !externalId || externalId === '') { - error(400, 'Invalid Request') + error(400, 'Invalid Request'); } - console.log('Creating or updating minimal game data', JSON.stringify(game, null, 2)) - const externalUrl = `https://boardgamegeek.com/boardgame/${externalId}` + console.log('Creating or updating minimal game data', JSON.stringify(game, null, 2)); + const externalUrl = `https://boardgamegeek.com/boardgame/${externalId}`; try { - let dbGames: Games[] = [] - console.log('Creating game', JSON.stringify(game, null, 2)) + let dbGames: Games[] = []; + console.log('Creating game', JSON.stringify(game, null, 2)); await db.transaction(async (transaction) => { dbGames = await transaction .insert(gamesTable) @@ -147,7 +147,7 @@ export async function createOrUpdateGameMinimal(locals: App.Locals, game: Games, max_playtime: game.max_playtime, }, }) - .returning() + .returning(); const dbExternalIds = await transaction .insert(externalIdsTable) .values({ @@ -155,42 +155,42 @@ export async function createOrUpdateGameMinimal(locals: App.Locals, game: Games, type: 'game', }) .onConflictDoNothing() - .returning({ id: externalIdsTable.id }) + .returning({ id: externalIdsTable.id }); await transaction .insert(gamesToExternalIdsTable) .values({ gameId: dbGames[0].id, externalId: dbExternalIds[0].id, }) - .onConflictDoNothing() - }) + .onConflictDoNothing(); + }); if (dbGames.length === 0) { return new Response('Could not create game', { status: 500, - }) + }); } - console.log('Created game', JSON.stringify(dbGames[0], null, 2)) + console.log('Created game', JSON.stringify(dbGames[0], null, 2)); return new Response(JSON.stringify(dbGames[0]), { status: 201, - }) + }); } catch (e) { - console.error(e) - throw new Error('Something went wrong creating Game') + console.error(e); + throw new Error('Something went wrong creating Game'); } } export async function createOrUpdateGame(locals: App.Locals, game: Games, externalId: string) { if (!game || !externalId || externalId === '') { - error(400, 'Invalid Request') + error(400, 'Invalid Request'); } try { - const externalUrl = `https://boardgamegeek.com/boardgame/${externalId}` + const externalUrl = `https://boardgamegeek.com/boardgame/${externalId}`; const dbExternalId = await db.query.externalIds.findFirst({ where: eq(externalIdsTable.externalId, externalId), - }) + }); if (dbExternalId) { const foundGame = await db @@ -200,22 +200,22 @@ export async function createOrUpdateGame(locals: App.Locals, game: Games, extern slug: gamesTable.slug, }) .from(gamesTable) - .leftJoin(gamesToExternalIdsTable, eq(gamesToExternalIdsTable.externalId, externalId)) - console.log('Game already exists', foundGame) + .leftJoin(gamesToExternalIdsTable, eq(gamesToExternalIdsTable.externalId, externalId)); + console.log('Game already exists', foundGame); if (foundGame.length > 0) { - console.log('Game name', foundGame[0].name) + console.log('Game name', foundGame[0].name); return new Response('Game already exists', { headers: { 'Content-Type': 'application/json', Location: `${PUBLIC_SITE_URL}/api/game/${foundGame[0].id}`, }, status: 409, - }) + }); } } - let dbGames: Games[] = [] - console.log('Creating game', JSON.stringify(game, null, 2)) + let dbGames: Games[] = []; + console.log('Creating game', JSON.stringify(game, null, 2)); await db.transaction(async (transaction) => { dbGames = await transaction .insert(gamesTable) @@ -250,7 +250,7 @@ export async function createOrUpdateGame(locals: App.Locals, game: Games, extern max_playtime: game.max_playtime, }, }) - .returning() + .returning(); const dbExternalIds = await transaction .insert(externalIdsTable) .values({ @@ -258,35 +258,35 @@ export async function createOrUpdateGame(locals: App.Locals, game: Games, extern type: 'game', }) .onConflictDoNothing() - .returning({ id: externalIdsTable.id }) + .returning({ id: externalIdsTable.id }); await transaction .insert(gamesToExternalIdsTable) .values({ gameId: dbGames[0].id, externalId: dbExternalIds[0].id, }) - .onConflictDoNothing() - }) + .onConflictDoNothing(); + }); if (dbGames.length === 0) { return new Response('Could not create game', { status: 500, - }) + }); } - console.log('Created game', JSON.stringify(dbGames[0], null, 2)) + console.log('Created game', JSON.stringify(dbGames[0], null, 2)); return new Response(JSON.stringify(dbGames[0]), { status: 201, - }) + }); } catch (e) { - console.error(e) - throw new Error('Something went wrong creating Game') + console.error(e); + throw new Error('Something went wrong creating Game'); } } export async function updateGame(locals: App.Locals, game: Games, id: string) { if (!game || !id || id === '') { - error(400, 'Invalid Request') + error(400, 'Invalid Request'); } try { @@ -307,17 +307,17 @@ export async function updateGame(locals: App.Locals, game: Games, id: string) { max_playtime: game.max_playtime, }) .where(eq(gamesTable.id, id)) - .returning() + .returning(); return new Response(JSON.stringify(dbGame[0]), { headers: { 'Content-Type': 'application/json', }, - }) + }); } catch (e) { - console.error(e) + console.error(e); return new Response('Could not get publishersTable', { status: 500, - }) + }); } } diff --git a/src/lib/utils/db/mechanicUtils.ts b/src/lib/utils/db/mechanicUtils.ts index 071c9c9..90dcc17 100644 --- a/src/lib/utils/db/mechanicUtils.ts +++ b/src/lib/utils/db/mechanicUtils.ts @@ -1,19 +1,19 @@ -import { PUBLIC_SITE_URL } from '$env/static/public' -import { type Mechanics, externalIdsTable, mechanicsTable, mechanicsToExternalIdsTable } from '$lib/server/api/databases/tables' -import { db } from '$lib/server/api/packages/drizzle' -import { error } from '@sveltejs/kit' -import { eq } from 'drizzle-orm' -import kebabCase from 'just-kebab-case' +import { PUBLIC_SITE_URL } from '$env/static/public'; +import { db } from '$lib/server/api/packages/drizzle'; +import { error } from '@sveltejs/kit'; +import { eq } from 'drizzle-orm'; +import kebabCase from 'just-kebab-case'; +import { type Mechanics, externalIdsTable, mechanicsTable, mechanicsToExternalIdsTable } from '../../server/api/databases/postgres/tables'; export async function createMechanic(locals: App.Locals, mechanic: Mechanics, externalId: string) { if (!mechanic || !externalId || externalId === '') { - error(400, 'Invalid Request') + error(400, 'Invalid Request'); } try { const dbExternalId = await db.query.externalIds.findFirst({ where: eq(externalIdsTable.externalId, externalId), - }) + }); if (dbExternalId) { const foundMechanic = await db @@ -23,22 +23,22 @@ export async function createMechanic(locals: App.Locals, mechanic: Mechanics, ex slug: mechanicsTable.slug, }) .from(mechanicsTable) - .leftJoin(mechanicsToExternalIdsTable, eq(mechanicsToExternalIdsTable.externalId, externalId)) - console.log('Mechanic already exists', foundMechanic) + .leftJoin(mechanicsToExternalIdsTable, eq(mechanicsToExternalIdsTable.externalId, externalId)); + console.log('Mechanic already exists', foundMechanic); if (foundMechanic.length > 0) { - console.log('Mechanic name', foundMechanic[0].name) + console.log('Mechanic name', foundMechanic[0].name); return new Response('Mechanic already exists', { headers: { 'Content-Type': 'application/json', Location: `${PUBLIC_SITE_URL}/api/mechanic/${foundMechanic[0].id}`, }, status: 409, - }) + }); } } - let dbMechanics: Mechanics[] = [] - console.log('Creating mechanic', JSON.stringify(mechanic, null, 2)) + let dbMechanics: Mechanics[] = []; + console.log('Creating mechanic', JSON.stringify(mechanic, null, 2)); await db.transaction(async (transaction) => { dbMechanics = await transaction .insert(mechanicsTable) @@ -46,32 +46,32 @@ export async function createMechanic(locals: App.Locals, mechanic: Mechanics, ex name: mechanic.name, slug: kebabCase(mechanic.name || mechanic.slug || ''), }) - .returning() + .returning(); const dbExternalIds = await transaction .insert(externalIdsTable) .values({ externalId, type: 'mechanic', }) - .returning({ id: externalIdsTable.id }) + .returning({ id: externalIdsTable.id }); await transaction.insert(mechanicsToExternalIdsTable).values({ mechanicId: dbMechanics[0].id, externalId: dbExternalIds[0].id, - }) - }) + }); + }); if (dbMechanics.length === 0) { return new Response('Could not create mechanic', { status: 500, - }) + }); } - console.log('Created mechanic', JSON.stringify(dbMechanics[0], null, 2)) + console.log('Created mechanic', JSON.stringify(dbMechanics[0], null, 2)); return new Response(JSON.stringify(dbMechanics[0]), { status: 201, - }) + }); } catch (e) { - console.error(e) - throw new Error('Something went wrong creating Mechanic') + console.error(e); + throw new Error('Something went wrong creating Mechanic'); } } diff --git a/src/lib/utils/db/publisherUtils.ts b/src/lib/utils/db/publisherUtils.ts index 576094d..f06b0a2 100644 --- a/src/lib/utils/db/publisherUtils.ts +++ b/src/lib/utils/db/publisherUtils.ts @@ -1,25 +1,25 @@ -import { PUBLIC_SITE_URL } from '$env/static/public' -import { type Publishers, externalIdsTable, publishersTable, publishersToExternalIdsTable } from '$lib/server/api/databases/tables' -import { db } from '$lib/server/api/packages/drizzle' -import { error } from '@sveltejs/kit' -import { eq } from 'drizzle-orm' -import kebabCase from 'just-kebab-case' +import { PUBLIC_SITE_URL } from '$env/static/public'; +import { db } from '$lib/server/api/packages/drizzle'; +import { error } from '@sveltejs/kit'; +import { eq } from 'drizzle-orm'; +import kebabCase from 'just-kebab-case'; +import { type Publishers, externalIdsTable, publishersTable, publishersToExternalIdsTable } from '../../server/api/databases/postgres/tables'; export async function getPublisher(locals: App.Locals, id: string) { - const publisher = await db.select().from(publishersTable).where(eq(publishersTable.id, id)) + const publisher = await db.select().from(publishersTable).where(eq(publishersTable.id, id)); if (publisher.length === 0) { - error(404, 'not found') + error(404, 'not found'); } return new Response(JSON.stringify(publisher[0]), { headers: { 'Content-Type': 'application/json', }, - }) + }); } export async function updatePublisher(locals: App.Locals, publisher: Publishers, id: string) { if (!publisher || publisher.name === '' || !id || id === '') { - error(400, 'Invalid Request') + error(400, 'Invalid Request'); } try { @@ -30,29 +30,29 @@ export async function updatePublisher(locals: App.Locals, publisher: Publishers, slug: kebabCase(publisher.name || ''), }) .where(eq(publishersTable.id, id)) - .returning() + .returning(); return new Response(JSON.stringify(dbPublisher[0]), { headers: { 'Content-Type': 'application/json', }, - }) + }); } catch (e) { - console.error(e) + console.error(e); return new Response('Could not get publishersTable', { status: 500, - }) + }); } } export async function createPublisher(locals: App.Locals, publisher: Publishers, externalId: string) { if (!publisher || !externalId || externalId === '') { - error(400, 'Invalid Request') + error(400, 'Invalid Request'); } try { const dbExternalId = await db.query.externalIds.findFirst({ where: eq(externalIdsTable.externalId, externalId), - }) + }); if (dbExternalId) { const foundPublisher = await db @@ -62,22 +62,22 @@ export async function createPublisher(locals: App.Locals, publisher: Publishers, slug: publishersTable.slug, }) .from(publishersTable) - .leftJoin(publishersToExternalIdsTable, eq(publishersToExternalIdsTable.externalId, externalId)) - console.log('Publisher already exists', foundPublisher) + .leftJoin(publishersToExternalIdsTable, eq(publishersToExternalIdsTable.externalId, externalId)); + console.log('Publisher already exists', foundPublisher); if (foundPublisher.length > 0) { - console.log('Publisher name', foundPublisher[0].name) + console.log('Publisher name', foundPublisher[0].name); return new Response('Publisher already exists', { headers: { 'Content-Type': 'application/json', Location: `${PUBLIC_SITE_URL}/api/publisher/${foundPublisher[0].id}`, }, status: 409, - }) + }); } } - let dbPublishers: Publishers[] = [] - console.log('Creating publisher', JSON.stringify(publisher, null, 2)) + let dbPublishers: Publishers[] = []; + console.log('Creating publisher', JSON.stringify(publisher, null, 2)); await db.transaction(async (transaction) => { dbPublishers = await transaction .insert(publishersTable) @@ -85,32 +85,32 @@ export async function createPublisher(locals: App.Locals, publisher: Publishers, name: publisher.name, slug: kebabCase(publisher.name || publisher.slug || ''), }) - .returning() + .returning(); const dbExternalIds = await transaction .insert(externalIdsTable) .values({ externalId, type: 'publisher', }) - .returning({ id: externalIdsTable.id }) + .returning({ id: externalIdsTable.id }); await transaction.insert(publishersToExternalIdsTable).values({ publisherId: dbPublishers[0].id, externalId: dbExternalIds[0].id, - }) - }) + }); + }); if (dbPublishers.length === 0) { return new Response('Could not create publisher', { status: 500, - }) + }); } - console.log('Created publisher', JSON.stringify(dbPublishers[0], null, 2)) + console.log('Created publisher', JSON.stringify(dbPublishers[0], null, 2)); return new Response(JSON.stringify(dbPublishers[0]), { status: 201, - }) + }); } catch (e) { - console.error(e) - throw new Error('Something went wrong creating Publisher') + console.error(e); + throw new Error('Something went wrong creating Publisher'); } } diff --git a/src/routes/(app)/(protected)/admin/+layout.server.ts b/src/routes/(app)/(protected)/admin/+layout.server.ts index e5deb8d..2517426 100644 --- a/src/routes/(app)/(protected)/admin/+layout.server.ts +++ b/src/routes/(app)/(protected)/admin/+layout.server.ts @@ -1,16 +1,16 @@ -import { forbiddenMessage, notSignedInMessage } from '$lib/flashMessages' -import { user_roles } from '$lib/server/api/databases/tables' -import { db } from '$lib/server/api/packages/drizzle' -import { errorMessage } from '$lib/utils/superforms' -import { eq } from 'drizzle-orm' -import { loadFlash, redirect } from 'sveltekit-flash-message/server' +import { forbiddenMessage, notSignedInMessage } from '$lib/flashMessages'; +import { db } from '$lib/server/api/packages/drizzle'; +import { errorMessage } from '$lib/utils/superforms'; +import { eq } from 'drizzle-orm'; +import { loadFlash, redirect } from 'sveltekit-flash-message/server'; +import { user_roles } from '../../../../lib/server/api/databases/postgres/tables'; export const load = loadFlash(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 dbUserRoles = await db.query.user_roles.findMany({ @@ -22,13 +22,13 @@ export const load = loadFlash(async (event) => { }, }, }, - }) + }); - const containsAdminRole = dbUserRoles.some((userRole) => userRole?.role?.name === 'admin') + const containsAdminRole = dbUserRoles.some((userRole) => userRole?.role?.name === 'admin'); if (!dbUserRoles?.length || !containsAdminRole) { - console.log('Not an admin') - redirect(302, '/', forbiddenMessage, event) + console.log('Not an admin'); + redirect(302, '/', forbiddenMessage, event); } - return {} -}) + return {}; +}); diff --git a/src/routes/(app)/(protected)/admin/users/[id]/+page.server.ts b/src/routes/(app)/(protected)/admin/users/[id]/+page.server.ts index ab2f1cf..d870805 100644 --- a/src/routes/(app)/(protected)/admin/users/[id]/+page.server.ts +++ b/src/routes/(app)/(protected)/admin/users/[id]/+page.server.ts @@ -1,17 +1,17 @@ -import { forbiddenMessage, notSignedInMessage } from '$lib/flashMessages' -import { rolesTable, user_roles, usersTable } from '$lib/server/api/databases/tables' -import { db } from '$lib/server/api/packages/drizzle' -import { and, eq, inArray, not } from 'drizzle-orm' -import { redirect } from 'sveltekit-flash-message/server' -import type { PageServerLoad } from './$types' +import { forbiddenMessage, notSignedInMessage } from '$lib/flashMessages'; +import { db } from '$lib/server/api/packages/drizzle'; +import { and, eq, inArray, not } from 'drizzle-orm'; +import { redirect } from 'sveltekit-flash-message/server'; +import { rolesTable, user_roles, usersTable } from '../../../../../../lib/server/api/databases/postgres/tables'; +import type { PageServerLoad } from './$types'; export const load: PageServerLoad = async (event) => { - const { params, locals } = event - const { id } = params + const { params, locals } = event; + const { id } = params; - 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 foundUser = await db.query.usersTable.findFirst({ @@ -28,16 +28,16 @@ export const load: PageServerLoad = async (event) => { }, }, }, - }) + }); - const containsAdminRole = foundUser?.user_roles?.some((user_role) => user_role?.role?.name === 'admin') + const containsAdminRole = foundUser?.user_roles?.some((user_role) => user_role?.role?.name === 'admin'); if (!containsAdminRole) { - console.log('Not an admin') - redirect(302, '/login', notSignedInMessage, event) + console.log('Not an admin'); + redirect(302, '/login', notSignedInMessage, event); } - const currentRoleIds = foundUser?.user_roles?.map((user_role) => user_role?.role.cuid) || [] - let availableRoles: { name: string; cuid: string }[] = [] + const currentRoleIds = foundUser?.user_roles?.map((user_role) => user_role?.role.cuid) || []; + let availableRoles: { name: string; cuid: string }[] = []; if (currentRoleIds?.length > 0) { availableRoles = await db.query.roles.findMany({ where: not(inArray(rolesTable.cuid, currentRoleIds)), @@ -45,22 +45,22 @@ export const load: PageServerLoad = async (event) => { name: true, cuid: true, }, - }) + }); } return { user: foundUser, availableRoles, - } -} + }; +}; export const actions = { addRole: async (event) => { - const { request, locals } = event - const { user } = locals + const { request, locals } = event; + const { user } = locals; if (!user) { - redirect(302, '/login', notSignedInMessage, event) + redirect(302, '/login', notSignedInMessage, event); } const userRolesList = await db.query.user_roles.findMany({ @@ -73,37 +73,37 @@ export const actions = { }, }, }, - }) + }); - console.log('userRoles', userRolesList) + console.log('userRoles', userRolesList); - const containsAdminRole = userRolesList.some((user_role) => user_role?.role?.name === 'admin') - console.log('containsAdminRole', containsAdminRole) + const containsAdminRole = userRolesList.some((user_role) => user_role?.role?.name === 'admin'); + console.log('containsAdminRole', containsAdminRole); if (!containsAdminRole) { - redirect(302, '/', forbiddenMessage, event) + redirect(302, '/', forbiddenMessage, event); } - const data = await request.formData() - const role = data.get('role') + const data = await request.formData(); + const role = data.get('role'); const dbRole = await db.query.roles.findFirst({ where: eq(rolesTable.cuid, role?.toString() ?? ''), - }) - console.log('dbRole', dbRole) + }); + console.log('dbRole', dbRole); if (dbRole) { await db.insert(user_roles).values({ user_id: user.id, role_id: dbRole.id, - }) - redirect({ type: 'success', message: `Successfully added role ${dbRole.name}!` }, event) + }); + redirect({ type: 'success', message: `Successfully added role ${dbRole.name}!` }, event); } else { - redirect({ type: 'error', message: `Failed to add role ${role?.toString()}!` }, event) + redirect({ type: 'error', message: `Failed to add role ${role?.toString()}!` }, event); } }, removeRole: async (event) => { - const { request, locals } = event - const { user } = locals + const { request, locals } = event; + const { user } = locals; if (!user) { - redirect(302, '/login', notSignedInMessage, event) + redirect(302, '/login', notSignedInMessage, event); } const userRolesList = await db.query.user_roles.findMany({ @@ -116,24 +116,24 @@ export const actions = { }, }, }, - }) + }); - const containsAdminRole = userRolesList.some((user_role) => user_role?.role?.name === 'admin') + const containsAdminRole = userRolesList.some((user_role) => user_role?.role?.name === 'admin'); if (!containsAdminRole) { - redirect(302, '/', forbiddenMessage, event) + redirect(302, '/', forbiddenMessage, event); } - const data = await request.formData() - const role = data.get('role') + const data = await request.formData(); + const role = data.get('role'); const dbRole = await db.query.roles.findFirst({ where: eq(rolesTable.cuid, role?.toString() ?? ''), - }) - console.log('dbRole', dbRole) + }); + console.log('dbRole', dbRole); if (dbRole) { - await db.delete(user_roles).where(and(eq(user_roles.user_id, user.id), eq(user_roles.role_id, dbRole.id))) - redirect({ type: 'success', message: `Successfully removed role ${dbRole.name}!` }, event) + await db.delete(user_roles).where(and(eq(user_roles.user_id, user.id), eq(user_roles.role_id, dbRole.id))); + redirect({ type: 'success', message: `Successfully removed role ${dbRole.name}!` }, event); } else { - redirect({ type: 'error', message: `Failed to remove role ${role?.toString()} !` }, event) + redirect({ type: 'error', message: `Failed to remove role ${role?.toString()} !` }, event); } }, -} +}; diff --git a/src/routes/(app)/(protected)/collections/+page.server.ts b/src/routes/(app)/(protected)/collections/+page.server.ts index 881f050..4e644c3 100644 --- a/src/routes/(app)/(protected)/collections/+page.server.ts +++ b/src/routes/(app)/(protected)/collections/+page.server.ts @@ -1,52 +1,52 @@ -import { notSignedInMessage } from '$lib/flashMessages' -import { collection_items, collections, gamesTable } from '$lib/server/api/databases/tables' -import { db } from '$lib/server/api/packages/drizzle' -import { modifyListGameSchema } from '$lib/validations/zod-schemas' -import { type Actions, error, fail } from '@sveltejs/kit' -import { and, eq } from 'drizzle-orm' -import { redirect } from 'sveltekit-flash-message/server' -import { zod } from 'sveltekit-superforms/adapters' -import { superValidate } from 'sveltekit-superforms/server' +import { notSignedInMessage } from '$lib/flashMessages'; +import { db } from '$lib/server/api/packages/drizzle'; +import { modifyListGameSchema } from '$lib/validations/zod-schemas'; +import { type Actions, error, fail } from '@sveltejs/kit'; +import { and, eq } from 'drizzle-orm'; +import { redirect } from 'sveltekit-flash-message/server'; +import { zod } from 'sveltekit-superforms/adapters'; +import { superValidate } from 'sveltekit-superforms/server'; +import { collection_items, collections, gamesTable } from '../../../../lib/server/api/databases/postgres/tables'; export async function load(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); } try { - const { data, error } = await locals.api.collections.$get().then(locals.parseApiResponse) + const { data, error } = await locals.api.collections.$get().then(locals.parseApiResponse); return { collections: data?.collections || [], - } + }; } catch (e) { - console.error(e) + console.error(e); } return { collections: [], - } + }; } export const actions: Actions = { // Add game to a wishlist add: 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 form = await superValidate(event, zod(modifyListGameSchema)) + const form = await superValidate(event, zod(modifyListGameSchema)); - const user = event.locals.user + const user = event.locals.user; const game = await db.query.gamesTable.findFirst({ where: eq(gamesTable.id, form.data.id), - }) + }); if (!game) { // game = await prisma.game.create({ @@ -54,86 +54,86 @@ export const actions: Actions = { // name: form.name // } // }); - console.log('game not found') - redirect(302, '/404') + console.log('game not found'); + redirect(302, '/404'); } try { const collection = await db.query.collections.findFirst({ where: eq(collections.user_id, user.id), - }) + }); if (!collection) { - console.log('Wishlist not found') - return error(404, 'Wishlist not found') + console.log('Wishlist not found'); + return error(404, 'Wishlist not found'); } await db.insert(collection_items).values({ game_id: game.id, collection_id: collection.id, times_played: 0, - }) + }); return { form, - } + }; } catch (e) { - console.error(e) - return error(500, 'Something went wrong') + console.error(e); + return error(500, 'Something went wrong'); } }, // Create new wishlist create: async ({ locals }) => { if (!locals.user) { - throw fail(401) + throw fail(401); } - return error(405, 'Method not allowed') + return error(405, 'Method not allowed'); }, // Delete a wishlist delete: async ({ locals }) => { if (!locals.user) { - throw fail(401) + throw fail(401); } - return error(405, 'Method not allowed') + return error(405, 'Method not allowed'); }, // Remove game from a wishlist remove: 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 form = await superValidate(event, zod(modifyListGameSchema)) + const form = await superValidate(event, zod(modifyListGameSchema)); const game = await db.query.gamesTable.findFirst({ where: eq(gamesTable.id, form.data.id), - }) + }); if (!game) { - console.log('game not found') - redirect(302, '/404') + console.log('game not found'); + redirect(302, '/404'); } try { const collection = await db.query.collections.findFirst({ where: eq(collections.user_id, authedUser.id), - }) + }); if (!collection) { - console.log('Collection not found') - return error(404, 'Collection not found') + console.log('Collection not found'); + return error(404, 'Collection not found'); } - await db.delete(collection_items).where(and(eq(collection_items.collection_id, collection.id), eq(collection_items.game_id, game.id))) + await db.delete(collection_items).where(and(eq(collection_items.collection_id, collection.id), eq(collection_items.game_id, game.id))); return { form, - } + }; } catch (e) { - console.error(e) - return error(500, 'Something went wrong') + console.error(e); + return error(500, 'Something went wrong'); } }, -} +}; diff --git a/src/routes/(app)/(protected)/collections/[cuid]/+page.server.ts b/src/routes/(app)/(protected)/collections/[cuid]/+page.server.ts index 488fbb1..34f0fe1 100644 --- a/src/routes/(app)/(protected)/collections/[cuid]/+page.server.ts +++ b/src/routes/(app)/(protected)/collections/[cuid]/+page.server.ts @@ -1,20 +1,20 @@ -import { notSignedInMessage } from '$lib/flashMessages.js' -import { collection_items, collections, gamesTable } from '$lib/server/api/databases/tables' -import { db } from '$lib/server/api/packages/drizzle' -import { modifyListGameSchema } from '$lib/validations/zod-schemas' -import { type Actions, error } from '@sveltejs/kit' -import { and, eq } from 'drizzle-orm' -import { redirect } from 'sveltekit-flash-message/server' -import { zod } from 'sveltekit-superforms/adapters' -import { superValidate } from 'sveltekit-superforms/server' +import { notSignedInMessage } from '$lib/flashMessages.js'; +import { db } from '$lib/server/api/packages/drizzle'; +import { modifyListGameSchema } from '$lib/validations/zod-schemas'; +import { type Actions, error } from '@sveltejs/kit'; +import { and, eq } from 'drizzle-orm'; +import { redirect } from 'sveltekit-flash-message/server'; +import { zod } from 'sveltekit-superforms/adapters'; +import { superValidate } from 'sveltekit-superforms/server'; +import { collection_items, collections, gamesTable } from '../../../../../lib/server/api/databases/postgres/tables'; export async function load(event) { - const { params, locals } = event - const { cuid } = params + const { params, locals } = event; + const { cuid } = params; - const authedUser = await locals.getAuthedUser() + const authedUser = await locals.getAuthedUser(); if (!authedUser) { - throw redirect(302, '/login', notSignedInMessage, event) + throw redirect(302, '/login', notSignedInMessage, event); } try { @@ -22,28 +22,28 @@ export async function load(event) { .$get({ param: { cuid }, }) - .then(locals.parseApiResponse) + .then(locals.parseApiResponse); if (errors) { - return error(500, 'Failed to fetch collection') + return error(500, 'Failed to fetch collection'); } - const { collection } = data + const { collection } = data; if (!collection) { - redirect(302, '/404') + redirect(302, '/404'); } - console.log('collection', collection) + console.log('collection', collection); return { collection, - } + }; } catch (e) { - console.error(e) + console.error(e); } - redirect(302, '/404') + redirect(302, '/404'); // const searchParams = Object.fromEntries(url?.searchParams); // console.log('searchParams', searchParams); @@ -129,18 +129,18 @@ export async function load(event) { export const actions: Actions = { // Add game to a wishlist add: 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 form = await superValidate(event, zod(modifyListGameSchema)) + const form = await superValidate(event, zod(modifyListGameSchema)); const game = await db.query.gamesTable.findFirst({ where: eq(gamesTable.id, form.data.id), - }) + }); if (!game) { // game = await prisma.game.create({ @@ -148,92 +148,92 @@ export const actions: Actions = { // name: form.name // } // }); - console.log('game not found') - redirect(302, '/404') + console.log('game not found'); + redirect(302, '/404'); } try { const collection = await db.query.collections.findFirst({ where: eq(collections.user_id, authedUser.id), - }) + }); if (!collection) { - console.log('Wishlist not found') - return error(404, 'Wishlist not found') + console.log('Wishlist not found'); + return error(404, 'Wishlist not found'); } await db.insert(collection_items).values({ game_id: game.id, collection_id: collection.id, times_played: 0, - }) + }); return { form, - } + }; } catch (e) { - console.error(e) - return error(500, 'Something went wrong') + console.error(e); + return error(500, 'Something went wrong'); } }, // Create new wishlist create: 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); } - return error(405, 'Method not allowed') + return error(405, 'Method not allowed'); }, // Delete a wishlist delete: 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); } - return error(405, 'Method not allowed') + return error(405, 'Method not allowed'); }, // Remove game from a wishlist remove: 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 form = await superValidate(event, zod(modifyListGameSchema)) + const form = await superValidate(event, zod(modifyListGameSchema)); const game = await db.query.gamesTable.findFirst({ where: eq(gamesTable.id, form.data.id), - }) + }); if (!game) { - console.log('game not found') - redirect(302, '/404') + console.log('game not found'); + redirect(302, '/404'); } try { const collection = await db.query.collections.findFirst({ where: eq(collections.user_id, authedUser.id), - }) + }); if (!collection) { - console.log('Collection not found') - return error(404, 'Collection not found') + console.log('Collection not found'); + return error(404, 'Collection not found'); } - await db.delete(collection_items).where(and(eq(collection_items.collection_id, collection.id), eq(collection_items.game_id, game.id))) + await db.delete(collection_items).where(and(eq(collection_items.collection_id, collection.id), eq(collection_items.game_id, game.id))); return { form, - } + }; } catch (e) { - console.error(e) - return error(500, 'Something went wrong') + console.error(e); + return error(500, 'Something went wrong'); } }, -} +}; diff --git a/src/routes/(app)/(protected)/list/+layout.server.ts b/src/routes/(app)/(protected)/list/+layout.server.ts index fd76692..8393342 100644 --- a/src/routes/(app)/(protected)/list/+layout.server.ts +++ b/src/routes/(app)/(protected)/list/+layout.server.ts @@ -1,29 +1,29 @@ -import { notSignedInMessage } from '$lib/flashMessages' -import { wishlistsTable } from '$lib/server/api/databases/tables' -import { db } from '$lib/server/api/packages/drizzle' -import { eq } from 'drizzle-orm' -import { redirect } from 'sveltekit-flash-message/server' +import { notSignedInMessage } from '$lib/flashMessages'; +import { db } from '$lib/server/api/packages/drizzle'; +import { eq } from 'drizzle-orm'; +import { redirect } from 'sveltekit-flash-message/server'; +import { wishlistsTable } from '../../../../lib/server/api/databases/postgres/tables'; export async function load(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); } try { const dbWishlists = await db.query.wishlists.findMany({ where: eq(wishlistsTable.user_id, authedUser.id), - }) + }); return { wishlists: dbWishlists, - } + }; } catch (e) { - console.error(e) + console.error(e); } return { wishlists: [], - } + }; } diff --git a/src/routes/(app)/(protected)/list/[id]/+page.server.ts b/src/routes/(app)/(protected)/list/[id]/+page.server.ts index 746362c..a5febe2 100644 --- a/src/routes/(app)/(protected)/list/[id]/+page.server.ts +++ b/src/routes/(app)/(protected)/list/[id]/+page.server.ts @@ -1,20 +1,20 @@ -import { notSignedInMessage } from '$lib/flashMessages' -import { gamesTable, wishlist_items, wishlistsTable } from '$lib/server/api/databases/tables' -import { db } from '$lib/server/api/packages/drizzle' -import { userNotAuthenticated } from '$lib/server/auth-utils' -import { modifyListGameSchema } from '$lib/validations/zod-schemas' -import { type Actions, fail } from '@sveltejs/kit' -import { eq } from 'drizzle-orm' -import { redirect } from 'sveltekit-flash-message/server' -import { zod } from 'sveltekit-superforms/adapters' -import { superValidate } from 'sveltekit-superforms/server' +import { notSignedInMessage } from '$lib/flashMessages'; +import { db } from '$lib/server/api/packages/drizzle'; +import { userNotAuthenticated } from '$lib/server/auth-utils'; +import { modifyListGameSchema } from '$lib/validations/zod-schemas'; +import { type Actions, fail } from '@sveltejs/kit'; +import { eq } from 'drizzle-orm'; +import { redirect } from 'sveltekit-flash-message/server'; +import { zod } from 'sveltekit-superforms/adapters'; +import { superValidate } from 'sveltekit-superforms/server'; +import { gamesTable, wishlist_items, wishlistsTable } from '../../../../../lib/server/api/databases/postgres/tables'; export async function load(event) { - const { params, locals } = event + const { params, 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); } try { @@ -31,101 +31,101 @@ export async function load(event) { .from(wishlistsTable) .leftJoin(wishlist_items, eq(wishlistsTable.id, wishlist_items.wishlist_id)) .leftJoin(gamesTable, eq(gamesTable.id, wishlist_items.game_id)) - .where(eq(wishlistsTable.id, params.id)) + .where(eq(wishlistsTable.id, params.id)); return { wishlist, - } + }; } catch (e) { - console.error(e) - return {} + console.error(e); + return {}; } } export const actions: Actions = { // Add game to a wishlist add: async (event) => { - const { locals, params } = event + const { locals, params } = 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 form = await superValidate(event, zod(modifyListGameSchema)) + const form = await superValidate(event, zod(modifyListGameSchema)); if (!locals.user) { - throw fail(401) + throw fail(401); } if (!params?.id) { throw fail(400, { message: 'Invalid Request', - }) + }); } const game = await db.query.gamesTable.findFirst({ where: eq(gamesTable.id, form.id), - }) + }); if (!game) { return fail(400, { message: 'Game not found', - }) + }); } const wishlist = await db.query.wishlistsTable.findFirst({ where: eq(wishlistsTable.id, params.id), - }) + }); if (wishlist?.user_id !== locals.user.id) { return fail(401, { message: 'Unauthorized', - }) + }); } if (!wishlist) { - redirect(302, '/404') + redirect(302, '/404'); } const wishlistItem = await db.insert(wishlist_items).values({ game_id: game.id, wishlist_id: wishlist.id, - }) + }); if (!wishlistItem) { return fail(500, { message: 'Something went wrong', - }) + }); } return { form, - } + }; }, // Create new wishlist create: 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); } }, // Delete a wishlist delete: 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); } }, // Remove game from a wishlist remove: 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); } }, -} +}; diff --git a/src/routes/(app)/(protected)/settings/profile/+page.server.ts b/src/routes/(app)/(protected)/settings/profile/+page.server.ts index 3651da5..01339e4 100644 --- a/src/routes/(app)/(protected)/settings/profile/+page.server.ts +++ b/src/routes/(app)/(protected)/settings/profile/+page.server.ts @@ -1,5 +1,4 @@ import { notSignedInMessage } from '$lib/flashMessages'; -import { usersTable } from '$lib/server/api/databases/tables'; import { db } from '$lib/server/api/packages/drizzle'; import { type Actions, fail } from '@sveltejs/kit'; import { eq } from 'drizzle-orm'; @@ -7,6 +6,7 @@ import { redirect } from 'sveltekit-flash-message/server'; import { zod } from 'sveltekit-superforms/adapters'; import { message, setError, superValidate } from 'sveltekit-superforms/server'; import { z } from 'zod'; +import { usersTable } from '../../../../../lib/server/api/databases/postgres/tables'; import type { PageServerLoad } from './$types'; import { updateEmailFormSchema, updateProfileFormSchema } from './schemas'; diff --git a/src/routes/(app)/(protected)/wishlists/+page.server.ts b/src/routes/(app)/(protected)/wishlists/+page.server.ts index d5e1386..7d5e2a4 100644 --- a/src/routes/(app)/(protected)/wishlists/+page.server.ts +++ b/src/routes/(app)/(protected)/wishlists/+page.server.ts @@ -1,49 +1,49 @@ -import { notSignedInMessage } from '$lib/flashMessages.js' -import { gamesTable, wishlist_items, wishlistsTable } from '$lib/server/api/databases/tables' -import { db } from '$lib/server/api/packages/drizzle' -import { modifyListGameSchema } from '$lib/validations/zod-schemas' -import { type Actions, error, fail } from '@sveltejs/kit' -import { and, eq } from 'drizzle-orm' -import { redirect } from 'sveltekit-flash-message/server' -import { zod } from 'sveltekit-superforms/adapters' -import { superValidate } from 'sveltekit-superforms/server' +import { notSignedInMessage } from '$lib/flashMessages.js'; +import { db } from '$lib/server/api/packages/drizzle'; +import { modifyListGameSchema } from '$lib/validations/zod-schemas'; +import { type Actions, error, fail } from '@sveltejs/kit'; +import { and, eq } from 'drizzle-orm'; +import { redirect } from 'sveltekit-flash-message/server'; +import { zod } from 'sveltekit-superforms/adapters'; +import { superValidate } from 'sveltekit-superforms/server'; +import { gamesTable, wishlist_items, wishlistsTable } from '../../../../lib/server/api/databases/postgres/tables'; export async function load(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 { data } = await locals.api.wishlists.$get().then(locals.parseApiResponse) - const userWishlists = data?.wishlists + const { data } = await locals.api.wishlists.$get().then(locals.parseApiResponse); + const userWishlists = data?.wishlists; if (userWishlists?.length === 0) { - console.log('Wishlists not found') - return fail(404, {}) + console.log('Wishlists not found'); + return fail(404, {}); } return { wishlists: userWishlists, - } + }; } export const actions: Actions = { // Add game to a wishlist add: 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 form = await superValidate(event, zod(modifyListGameSchema)) + const form = await superValidate(event, zod(modifyListGameSchema)); try { const game = await db.query.games.findFirst({ where: eq(gamesTable.id, form.data.id), - }) + }); if (!game) { // game = await prisma.game.create({ @@ -51,68 +51,68 @@ export const actions: Actions = { // name: form.name // } // }); - console.log('game not found') - redirect(302, '/404') + console.log('game not found'); + redirect(302, '/404'); } if (game) { const wishlist = await db.query.wishlists.findFirst({ where: eq(wishlistsTable.user_id, authedUser.id), - }) + }); if (!wishlist) { - console.log('Wishlist not found') - return error(404, 'Wishlist not found') + console.log('Wishlist not found'); + return error(404, 'Wishlist not found'); } await db.insert(wishlist_items).values({ game_id: game.id, wishlist_id: wishlist.id, - }) + }); } return { form, - } + }; } catch (e) { - console.error(e) - return error(500, 'Something went wrong') + console.error(e); + return error(500, 'Something went wrong'); } }, // Create new wishlist create: 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); } - return error(405, 'Method not allowed') + return error(405, 'Method not allowed'); }, // Delete a wishlist delete: 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); } - return error(405, 'Method not allowed') + return error(405, 'Method not allowed'); }, // Remove game from a wishlist remove: 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 form = await superValidate(event, zod(modifyListGameSchema)) + const form = await superValidate(event, zod(modifyListGameSchema)); try { const game = await db.query.gamesTable.findFirst({ where: eq(gamesTable.id, form.data.id), - }) + }); if (!game) { // game = await prisma.game.create({ @@ -120,29 +120,29 @@ export const actions: Actions = { // name: form.name // } // }); - console.log('game not found') - redirect(302, '/404') + console.log('game not found'); + redirect(302, '/404'); } if (game) { const wishlist = await db.query.wishlistsTable.findFirst({ where: eq(wishlistsTable.user_id, authedUser.id), - }) + }); if (!wishlist) { - console.log('Wishlist not found') - return error(404, 'Wishlist not found') + console.log('Wishlist not found'); + return error(404, 'Wishlist not found'); } - await db.delete(wishlist_items).where(and(eq(wishlist_items.wishlist_id, wishlist.id), eq(wishlist_items.game_id, game.id))) + await db.delete(wishlist_items).where(and(eq(wishlist_items.wishlist_id, wishlist.id), eq(wishlist_items.game_id, game.id))); } return { form, - } + }; } catch (e) { - console.error(e) - return error(500, 'Something went wrong') + console.error(e); + return error(500, 'Something went wrong'); } }, -} +}; diff --git a/src/routes/(app)/(protected)/wishlists/[cuid]/+page.server.ts b/src/routes/(app)/(protected)/wishlists/[cuid]/+page.server.ts index 2ec8923..6bb229d 100644 --- a/src/routes/(app)/(protected)/wishlists/[cuid]/+page.server.ts +++ b/src/routes/(app)/(protected)/wishlists/[cuid]/+page.server.ts @@ -1,21 +1,21 @@ -import { notSignedInMessage } from '$lib/flashMessages.js' -import { db } from '$lib/server/api/packages/drizzle' -import { userNotAuthenticated } from '$lib/server/auth-utils' -import { modifyListGameSchema } from '$lib/validations/zod-schemas' -import { type Actions, error, fail } from '@sveltejs/kit' -import { and, eq } from 'drizzle-orm' -import { redirect } from 'sveltekit-flash-message/server' -import { zod } from 'sveltekit-superforms/adapters' -import { superValidate } from 'sveltekit-superforms/server' -import { gamesTable, wishlist_items, wishlistsTable } from '../../../../../lib/server/api/databases/tables' +import { notSignedInMessage } from '$lib/flashMessages.js'; +import { db } from '$lib/server/api/packages/drizzle'; +import { userNotAuthenticated } from '$lib/server/auth-utils'; +import { modifyListGameSchema } from '$lib/validations/zod-schemas'; +import { type Actions, error, fail } from '@sveltejs/kit'; +import { and, eq } from 'drizzle-orm'; +import { redirect } from 'sveltekit-flash-message/server'; +import { zod } from 'sveltekit-superforms/adapters'; +import { superValidate } from 'sveltekit-superforms/server'; +import { gamesTable, wishlist_items, wishlistsTable } from '../../../../../lib/server/api/databases/postgres/tables'; export async function load(event) { - const { params, locals } = event - const { cuid } = params + const { params, locals } = event; + const { cuid } = params; - const authedUser = await locals.getAuthedUser() + const authedUser = await locals.getAuthedUser(); if (!authedUser) { - throw redirect(302, '/login', notSignedInMessage, event) + throw redirect(302, '/login', notSignedInMessage, event); } try { @@ -23,45 +23,45 @@ export async function load(event) { .$get({ param: { cuid }, }) - .then(locals.parseApiResponse) + .then(locals.parseApiResponse); // const wishlist = await db.query.wishlistsTable.findMany({ // where: and(eq(wishlistsTable.user_id, authedUser.id), eq(wishlistsTable.cuid, cuid)), // }); if (errors) { - return error(500, 'Failed to fetch wishlist') + return error(500, 'Failed to fetch wishlist'); } - const { wishlist } = data + const { wishlist } = data; if (!wishlist) { - redirect(302, '/404') + redirect(302, '/404'); } - console.log('wishlist', wishlist) + console.log('wishlist', wishlist); return { wishlist, - } + }; } catch (e) { - console.error(e) + console.error(e); } - redirect(302, '/404') + redirect(302, '/404'); } export const actions: Actions = { // Add game to a wishlist add: 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 form = await superValidate(event, zod(modifyListGameSchema)) + const form = await superValidate(event, zod(modifyListGameSchema)); try { const game = await db.query.gamesTable.findFirst({ where: eq(gamesTable.id, form.data.id), - }) + }); if (!game) { // game = await prisma.game.create({ @@ -69,68 +69,68 @@ export const actions: Actions = { // name: form.name // } // }); - console.log('game not found') - redirect(302, '/404') + console.log('game not found'); + redirect(302, '/404'); } if (game) { const wishlist = await db.query.wishlistsTable.findFirst({ where: eq(wishlistsTable.user_id, authedUser.id), - }) + }); if (!wishlist) { - console.log('Wishlist not found') - return error(404, 'Wishlist not found') + console.log('Wishlist not found'); + return error(404, 'Wishlist not found'); } await db.insert(wishlist_items).values({ game_id: game.id, wishlist_id: wishlist.id, - }) + }); } return { form, - } + }; } catch (e) { - console.error(e) - return error(500, 'Something went wrong') + console.error(e); + return error(500, 'Something went wrong'); } }, // Create new wishlist create: 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); } - return error(405, 'Method not allowed') + return error(405, 'Method not allowed'); }, // Delete a wishlist delete: 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); } - return error(405, 'Method not allowed') + return error(405, 'Method not allowed'); }, // Remove game from a wishlist remove: 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 form = await superValidate(event, zod(modifyListGameSchema)) + const form = await superValidate(event, zod(modifyListGameSchema)); try { const game = await db.query.gamesTable.findFirst({ where: eq(gamesTable.id, form.data.id), - }) + }); if (!game) { // game = await prisma.game.create({ @@ -138,29 +138,29 @@ export const actions: Actions = { // name: form.name // } // }); - console.log('game not found') - redirect(302, '/404') + console.log('game not found'); + redirect(302, '/404'); } if (game) { const wishlist = await db.query.wishlistsTable.findFirst({ where: eq(wishlistsTable.user_id, authedUser.id), - }) + }); if (!wishlist) { - console.log('Wishlist not found') - return error(404, 'Wishlist not found') + console.log('Wishlist not found'); + return error(404, 'Wishlist not found'); } - await db.delete(wishlist_items).where(and(eq(wishlist_items.wishlist_id, wishlist.id), eq(wishlist_items.game_id, game.id))) + await db.delete(wishlist_items).where(and(eq(wishlist_items.wishlist_id, wishlist.id), eq(wishlist_items.game_id, game.id))); } return { form, - } + }; } catch (e) { - console.error(e) - return error(500, 'Something went wrong') + console.error(e); + return error(500, 'Something went wrong'); } }, -} +}; diff --git a/src/routes/(app)/game/[id]/+page.server.ts b/src/routes/(app)/game/[id]/+page.server.ts index 7e37c23..2c0d907 100644 --- a/src/routes/(app)/game/[id]/+page.server.ts +++ b/src/routes/(app)/game/[id]/+page.server.ts @@ -1,19 +1,26 @@ -import { collection_items, collections, expansionsTable, gamesTable, wishlist_items, wishlistsTable } from '$lib/server/api/databases/tables' -import { db } from '$lib/server/api/packages/drizzle' -import { createCategory } from '$lib/utils/db/categoryUtils' -import { createExpansion } from '$lib/utils/db/expansionUtils' -import { createOrUpdateGame } from '$lib/utils/db/gameUtils' -import { createMechanic } from '$lib/utils/db/mechanicUtils' -import { createPublisher } from '$lib/utils/db/publisherUtils' -import { mapAPIGameToBoredGame } from '$lib/utils/gameMapper.js' -import { error } from '@sveltejs/kit' -import { and, eq } from 'drizzle-orm' -import type { PageServerLoad } from './$types' +import { db } from '$lib/server/api/packages/drizzle'; +import { createCategory } from '$lib/utils/db/categoryUtils'; +import { createExpansion } from '$lib/utils/db/expansionUtils'; +import { createOrUpdateGame } from '$lib/utils/db/gameUtils'; +import { createMechanic } from '$lib/utils/db/mechanicUtils'; +import { createPublisher } from '$lib/utils/db/publisherUtils'; +import { mapAPIGameToBoredGame } from '$lib/utils/gameMapper.js'; +import { error } from '@sveltejs/kit'; +import { and, eq } from 'drizzle-orm'; +import { + collection_items, + collections, + expansionsTable, + gamesTable, + wishlist_items, + wishlistsTable, +} from '../../../../lib/server/api/databases/postgres/tables'; +import type { PageServerLoad } from './$types'; export const load: PageServerLoad = async ({ params, locals, fetch }) => { try { - const { user } = locals - const { id } = params + const { user } = locals; + const { id } = params; const game = await db.query.games.findFirst({ where: eq(gamesTable.id, id), with: { @@ -48,17 +55,17 @@ export const load: PageServerLoad = async ({ params, locals, fetch }) => { }, }, }, - }) - console.log('found game', game) + }); + console.log('found game', game); if (!game) { - error(404, 'not found') + error(404, 'not found'); } - const currentDate = new Date() + const currentDate = new Date(); if (game.last_sync_at === null || currentDate.getDate() - game.last_sync_at.getDate() > 7 * 24 * 60 * 60 * 1000) { - console.log('Syncing details because last sync is out of date') - await syncGameAndConnectedData(locals, game, fetch) + console.log('Syncing details because last sync is out of date'); + await syncGameAndConnectedData(locals, game, fetch); } const gameExpansions = await db.query.expansions.findMany({ @@ -72,36 +79,36 @@ export const load: PageServerLoad = async ({ params, locals, fetch }) => { }, }, }, - }) + }); - let collectionItem - let wishlistItem + let collectionItem; + let wishlistItem; if (user) { const wishlist = await db.query.wishlists.findFirst({ where: eq(wishlistsTable.user_id, user.id), - }) + }); // TODO: Select wishlist items based on wishlist if (wishlist) { wishlistItem = await db.query.wishlist_items.findFirst({ where: and(eq(wishlist_items.wishlist_id, wishlist.id), eq(wishlist_items.game_id, game.id)), - }) + }); } const collection = await db.query.collections.findFirst({ where: eq(collections.user_id, user.id), - }) + }); // TODO: Select collection items based on collection if (collection) { collectionItem = await db.query.collection_items.findFirst({ where: and(eq(collection_items.collection_id, collection.id), eq(collection_items.game_id, game.id)), - }) + }); } } - console.log('Returning game', game) + console.log('Returning game', game); return { game, @@ -109,53 +116,53 @@ export const load: PageServerLoad = async ({ params, locals, fetch }) => { user, in_wishlist: wishlistItem !== undefined || false, in_collection: collectionItem !== undefined || false, - } + }; } catch (error) { - console.log(error) + console.log(error); } - error(404, 'not found') -} + error(404, 'not found'); +}; async function syncGameAndConnectedData(locals: App.Locals, game: Game, eventFetch: Function) { - console.log(`Retrieving full external game details for external id: ${game.external_id} with name ${game.name}`) - const externalGameResponse = await eventFetch(`/api/external/game/${game.external_id}`) + console.log(`Retrieving full external game details for external id: ${game.external_id} with name ${game.name}`); + const externalGameResponse = await eventFetch(`/api/external/game/${game.external_id}`); if (externalGameResponse.ok) { - const externalGame = await externalGameResponse.json() - console.log('externalGame', externalGame) - const categories = [] - const mechanics = [] - const publishers = [] + const externalGame = await externalGameResponse.json(); + console.log('externalGame', externalGame); + const categories = []; + const mechanics = []; + const publishers = []; for (const externalCategory of externalGame.categories) { - const category = await createCategory(locals, externalCategory, externalGame.external_id) + const category = await createCategory(locals, externalCategory, externalGame.external_id); categories.push({ id: category.id, - }) + }); } for (const externalMechanic of externalGame.mechanics) { - const mechanic = await createMechanic(locals, externalMechanic, externalGame.external_id) - mechanics.push({ id: mechanic.id }) + const mechanic = await createMechanic(locals, externalMechanic, externalGame.external_id); + mechanics.push({ id: mechanic.id }); } for (const externalPublisher of externalGame.publishers) { - const publisher = await createPublisher(locals, externalPublisher, externalGame.external_id) - publishers.push({ id: publisher.id }) + const publisher = await createPublisher(locals, externalPublisher, externalGame.external_id); + publishers.push({ id: publisher.id }); } for (const externalExpansion of externalGame.expansions) { - console.log('Inbound?', externalExpansion.inbound) + console.log('Inbound?', externalExpansion.inbound); if (externalExpansion?.inbound === true) { - createExpansion(locals, externalExpansion) + createExpansion(locals, externalExpansion); } else { - createExpansion(locals, externalExpansion) + createExpansion(locals, externalExpansion); } } - const boredGame = mapAPIGameToBoredGame(externalGame) + const boredGame = mapAPIGameToBoredGame(externalGame); - boredGame.categories = categories - boredGame.mechanics = mechanics - boredGame.publishers = publishers + boredGame.categories = categories; + boredGame.mechanics = mechanics; + boredGame.publishers = publishers; // boredGame.expansionsTable = expansionsTable; - return createOrUpdateGame(locals, boredGame, externalGame.external_id) + return createOrUpdateGame(locals, boredGame, externalGame.external_id); } } diff --git a/src/routes/(auth)/totp/+page.server.ts b/src/routes/(auth)/totp/+page.server.ts index cfba820..568a690 100644 --- a/src/routes/(auth)/totp/+page.server.ts +++ b/src/routes/(auth)/totp/+page.server.ts @@ -1,45 +1,45 @@ -import { notSignedInMessage } from '$lib/flashMessages' -import env from '$lib/server/api/common/env' -import { twoFactorTable, usersTable } from '$lib/server/api/databases/tables' -import { db } from '$lib/server/api/packages/drizzle' -import { recoveryCodeSchema, totpSchema } from '$lib/validations/auth' -import { type Actions, fail } from '@sveltejs/kit' -import { eq } from 'drizzle-orm' -import { redirect } from 'sveltekit-flash-message/server' -import { zod } from 'sveltekit-superforms/adapters' -import { superValidate } from 'sveltekit-superforms/server' -import type { PageServerLoad, RequestEvent } from './$types' +import { notSignedInMessage } from '$lib/flashMessages'; +import env from '$lib/server/api/common/env'; +import { db } from '$lib/server/api/packages/drizzle'; +import { recoveryCodeSchema, totpSchema } from '$lib/validations/auth'; +import { type Actions, fail } from '@sveltejs/kit'; +import { eq } from 'drizzle-orm'; +import { redirect } from 'sveltekit-flash-message/server'; +import { zod } from 'sveltekit-superforms/adapters'; +import { superValidate } from 'sveltekit-superforms/server'; +import { twoFactorTable, usersTable } from '../../../lib/server/api/databases/postgres/tables'; +import type { PageServerLoad, RequestEvent } from './$types'; 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 dbUser = await db.query.usersTable.findFirst({ where: eq(usersTable.username, authedUser.username), - }) + }); const twoFactorDetails = await db.query.twoFactorTable.findFirst({ where: eq(twoFactorTable.userId, authedUser.id), - }) + }); if (!twoFactorDetails || !twoFactorDetails.enabled) { const message = { type: 'error', message: 'Two factor authentication is not enabled', - } as const - redirect(302, '/login', message, event) + } as const; + redirect(302, '/login', message, event); } - let twoFactorInitiatedTime = twoFactorDetails.initiatedTime + let twoFactorInitiatedTime = twoFactorDetails.initiatedTime; if (twoFactorInitiatedTime === null) { - console.log('twoFactorInitiatedTime is null') - twoFactorInitiatedTime = new Date() - console.log('twoFactorInitiatedTime', twoFactorInitiatedTime) - await db.update(twoFactorTable).set({ initiatedTime: twoFactorInitiatedTime }).where(eq(twoFactorTable.userId, dbUser!.id!)) + console.log('twoFactorInitiatedTime is null'); + twoFactorInitiatedTime = new Date(); + console.log('twoFactorInitiatedTime', twoFactorInitiatedTime); + await db.update(twoFactorTable).set({ initiatedTime: twoFactorInitiatedTime }).where(eq(twoFactorTable.userId, dbUser!.id!)); } // Check if two factor started less than TWO_FACTOR_TIMEOUT @@ -69,25 +69,25 @@ export const load: PageServerLoad = async (event) => { return { totpForm: await superValidate(event, zod(totpSchema)), recoveryCodeForm: await superValidate(event, zod(recoveryCodeSchema)), - } -} + }; +}; export const actions: Actions = { validateTotp: 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 { dbUser, twoFactorDetails } = await validateUserData(event, locals) + const { dbUser, twoFactorDetails } = await validateUserData(event, locals); - const totpForm = await superValidate(event, zod(totpSchema)) + const totpForm = await superValidate(event, zod(totpSchema)); if (!totpForm.valid) { - totpForm.data.totpToken = '' - return fail(400, { totpForm }) + totpForm.data.totpToken = ''; + return fail(400, { totpForm }); } // let sessionCookie @@ -144,23 +144,23 @@ export const actions: Actions = { // // totpForm.data.totpToken = '' // const message = { type: 'success', message: 'Signed In!' } as const - redirect(302, '/', message, event) + redirect(302, '/', message, event); }, validateRecoveryCode: async (event) => { - const { cookies, locals } = event + const { cookies, 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 { dbUser, twoFactorDetails } = await validateUserData(event, locals) + const { dbUser, twoFactorDetails } = await validateUserData(event, locals); - const recoveryCodeForm = await superValidate(event, zod(recoveryCodeSchema)) + const recoveryCodeForm = await superValidate(event, zod(recoveryCodeSchema)); if (!recoveryCodeForm.valid) { return fail(400, { form: recoveryCodeForm, - }) + }); } // let sessionCookie @@ -215,56 +215,56 @@ export const actions: Actions = { // ...sessionCookie.attributes, // }) - recoveryCodeForm.data.recoveryCode = '' - const message = { type: 'success', message: 'Signed In!' } as const - redirect(302, '/', message, event) + recoveryCodeForm.data.recoveryCode = ''; + const message = { type: 'success', message: 'Signed In!' } as const; + redirect(302, '/', message, event); }, -} +}; async function validateUserData(event: RequestEvent, locals: App.Locals) { - const { user, session } = locals + const { user, session } = locals; if (!user || !session) { - throw fail(401) + throw fail(401); } const dbUser = await db.query.usersTable.findFirst({ where: eq(usersTable.username, user.username), - }) + }); if (!dbUser) { - throw fail(401) + throw fail(401); } - const isTwoFactorAuthenticated = session?.isTwoFactorAuthenticated + const isTwoFactorAuthenticated = session?.isTwoFactorAuthenticated; const twoFactorDetails = await db.query.twoFactorTable.findFirst({ where: eq(twoFactorTable.userId, dbUser!.id!), - }) + }); if (!twoFactorDetails) { - const message = { type: 'error', message: 'Unable to process request' } as const - throw redirect(302, '/login', message, event) + const message = { type: 'error', message: 'Unable to process request' } as const; + throw redirect(302, '/login', message, event); } if (isTwoFactorAuthenticated && twoFactorDetails.enabled && twoFactorDetails.secret !== '') { - const message = { type: 'success', message: 'You are already signed in' } as const - throw redirect('/', message, event) + const message = { type: 'success', message: 'You are already signed in' } as const; + throw redirect('/', message, event); } - return { dbUser, twoFactorDetails } + return { dbUser, twoFactorDetails }; } function totpTimeElapsed(initiatedTime: Date) { if (initiatedTime === null || initiatedTime === undefined) { - return true + return true; } - const timeElapsed = Date.now() - initiatedTime.getTime() - console.log('Time elapsed', timeElapsed) + const timeElapsed = Date.now() - initiatedTime.getTime(); + console.log('Time elapsed', timeElapsed); if (timeElapsed > env.TWO_FACTOR_TIMEOUT) { - console.log('Time elapsed was more than TWO_FACTOR_TIMEOUT', timeElapsed, env.TWO_FACTOR_TIMEOUT) - return true + console.log('Time elapsed was more than TWO_FACTOR_TIMEOUT', timeElapsed, env.TWO_FACTOR_TIMEOUT); + return true; } - return false + return false; } // async function checkRecoveryCode(recoveryCode: string, userId: string) {