From 6677001d7ff33f2d1cbf76d8f234d6ef1cfa8070 Mon Sep 17 00:00:00 2001 From: Bradley Shellnut Date: Fri, 27 Dec 2024 16:19:21 -0800 Subject: [PATCH] Fixing seeds, migrations, and drizzle tables etc. --- drizzle.config.ts | 7 +- drizzle/0000_lowly_stone_men.sql | 68 +++ drizzle/meta/0000_snapshot.json | 451 ++++++++++++++++++ drizzle/meta/_journal.json | 13 + package.json | 8 +- pnpm-lock.yaml | 384 +++++++++++++-- .../api/common/configs/config.service.ts | 44 +- .../api/databases/postgres/drizzle-schema.ts | 3 + .../api/databases/postgres/drizzle.service.ts | 43 +- .../server/api/databases/postgres/migrate.ts | 37 ++ src/lib/server/api/databases/postgres/seed.ts | 50 ++ .../databases/postgres/seeds/data/roles.json | 11 + .../databases/postgres/seeds/data/users.json | 45 ++ .../api/databases/postgres/seeds/index.ts | 2 + .../api/databases/postgres/seeds/roles.ts | 11 + .../api/databases/postgres/seeds/users.ts | 82 ++++ .../api/mfa/tables/recovery-codes.table.ts | 46 ++ .../server/api/mfa/tables/two-factor.table.ts | 30 +- .../server/api/roles/tables/roles.table.ts | 21 +- .../api/users/tables/credentials.table.ts | 31 +- .../api/users/tables/user-roles.table.ts | 34 +- .../server/api/users/tables/users.table.ts | 12 +- src/lib/server/api/users/users.repository.ts | 38 +- 23 files changed, 1314 insertions(+), 157 deletions(-) create mode 100644 drizzle/0000_lowly_stone_men.sql create mode 100644 drizzle/meta/0000_snapshot.json create mode 100644 drizzle/meta/_journal.json create mode 100644 src/lib/server/api/databases/postgres/migrate.ts create mode 100644 src/lib/server/api/databases/postgres/seed.ts create mode 100644 src/lib/server/api/databases/postgres/seeds/data/roles.json create mode 100644 src/lib/server/api/databases/postgres/seeds/data/users.json create mode 100644 src/lib/server/api/databases/postgres/seeds/index.ts create mode 100644 src/lib/server/api/databases/postgres/seeds/roles.ts create mode 100644 src/lib/server/api/databases/postgres/seeds/users.ts create mode 100644 src/lib/server/api/mfa/tables/recovery-codes.table.ts diff --git a/drizzle.config.ts b/drizzle.config.ts index 6367408..7508c34 100644 --- a/drizzle.config.ts +++ b/drizzle.config.ts @@ -14,7 +14,12 @@ export default defineConfig({ dialect: 'postgresql', casing: 'snake_case', dbCredentials: { - url: process.env.DATABASE_URL ?? '', + host: process.env.DATABASE_HOST || 'localhost', + port: Number(process.env.DATABASE_PORT) || 5432, + user: process.env.DATABASE_USER, + password: process.env.DATABASE_PASSWORD, + database: process.env.DATABASE_DB || 'boredgame', + ssl: process.env.DATABASE_HOST !== 'localhost', }, migrations: { table: 'migrations', diff --git a/drizzle/0000_lowly_stone_men.sql b/drizzle/0000_lowly_stone_men.sql new file mode 100644 index 0000000..6c34424 --- /dev/null +++ b/drizzle/0000_lowly_stone_men.sql @@ -0,0 +1,68 @@ +CREATE EXTENSION IF NOT EXISTS citext; + +CREATE TABLE "credentials" ( + "id" text PRIMARY KEY NOT NULL, + "user_id" text NOT NULL, + "type" text DEFAULT 'password' NOT NULL, + "secret_data" text NOT NULL, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "updated_at" timestamp with time zone DEFAULT now() NOT NULL +); + +CREATE TABLE "roles" ( + "id" text PRIMARY KEY NOT NULL, + "name" text NOT NULL, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "updated_at" timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT "roles_name_unique" UNIQUE("name") +); + +CREATE TABLE "user_roles" ( + "id" text PRIMARY KEY NOT NULL, + "user_id" text NOT NULL, + "role_id" text NOT NULL, + "primary" boolean DEFAULT false, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "updated_at" timestamp with time zone DEFAULT now() NOT NULL +); + +CREATE TABLE "users" ( + "id" text PRIMARY KEY NOT NULL, + "username" text, + "email" "citext" NOT NULL, + "first_name" text, + "last_name" text, + "email_verified" boolean DEFAULT false, + "mfa_enabled" boolean DEFAULT false NOT NULL, + "avatar" text, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "updated_at" timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT "users_username_unique" UNIQUE("username"), + CONSTRAINT "users_email_unique" UNIQUE("email") +); + +CREATE TABLE "two_factor" ( + "id" text PRIMARY KEY NOT NULL, + "user_id" text NOT NULL, + "secret" text NOT NULL, + "enabled" boolean DEFAULT false NOT NULL, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "updated_at" timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT "two_factor_user_id_unique" UNIQUE("user_id") +); + +CREATE TABLE "recovery_codes" ( + "id" text PRIMARY KEY NOT NULL, + "user_id" text NOT NULL, + "code" text NOT NULL, + "used" boolean DEFAULT false, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "updated_at" timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT "recovery_codes_user_id_unique" UNIQUE("user_id") +); + +ALTER TABLE "credentials" ADD CONSTRAINT "credentials_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action; +ALTER TABLE "user_roles" ADD CONSTRAINT "user_roles_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action; +ALTER TABLE "user_roles" ADD CONSTRAINT "user_roles_role_id_roles_id_fk" FOREIGN KEY ("role_id") REFERENCES "public"."roles"("id") ON DELETE cascade ON UPDATE no action; +ALTER TABLE "two_factor" ADD CONSTRAINT "two_factor_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action; +ALTER TABLE "recovery_codes" ADD CONSTRAINT "recovery_codes_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action; \ No newline at end of file diff --git a/drizzle/meta/0000_snapshot.json b/drizzle/meta/0000_snapshot.json new file mode 100644 index 0000000..a1bb68b --- /dev/null +++ b/drizzle/meta/0000_snapshot.json @@ -0,0 +1,451 @@ +{ + "id": "c0bee96d-45d1-4f02-8b23-37c19afac692", + "prevId": "00000000-0000-0000-0000-000000000000", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.credentials": { + "name": "credentials", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "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.roles": { + "name": "roles", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "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_name_unique": { + "name": "roles_name_unique", + "nullsNotDistinct": false, + "columns": [ + "name" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_roles": { + "name": "user_roles", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role_id": { + "name": "role_id", + "type": "text", + "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": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.users": { + "name": "users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "email": { + "name": "email", + "type": "citext", + "primaryKey": false, + "notNull": true + }, + "first_name": { + "name": "first_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_name": { + "name": "last_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "email_verified": { + "name": "email_verified", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "mfa_enabled": { + "name": "mfa_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "avatar": { + "name": "avatar", + "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": { + "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.two_factor": { + "name": "two_factor", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "secret": { + "name": "secret", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "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": { + "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_user_id_unique": { + "name": "two_factor_user_id_unique", + "nullsNotDistinct": false, + "columns": [ + "user_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.recovery_codes": { + "name": "recovery_codes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "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": { + "recovery_codes_user_id_unique": { + "name": "recovery_codes_user_id_unique", + "nullsNotDistinct": false, + "columns": [ + "user_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": {}, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/drizzle/meta/_journal.json b/drizzle/meta/_journal.json new file mode 100644 index 0000000..414c03a --- /dev/null +++ b/drizzle/meta/_journal.json @@ -0,0 +1,13 @@ +{ + "version": "7", + "dialect": "postgresql", + "entries": [ + { + "idx": 0, + "version": "7", + "when": 1735345093448, + "tag": "0000_lowly_stone_men", + "breakpoints": false + } + ] +} \ No newline at end of file diff --git a/package.json b/package.json index d5ca415..152db7d 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,9 @@ "test:e2e": "playwright test", "db:start": "docker compose up", "db:push": "drizzle-kit push", - "db:migrate": "drizzle-kit migrate", + "db:generate": "drizzle-kit generate", + "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", "storybook": "storybook dev -p 6006", "build-storybook": "storybook build" @@ -43,6 +45,7 @@ "@types/cookie": "^1.0.0", "@types/node": "^22.10.2", "@types/pg": "^8.11.10", + "@types/pg-pool": "^2.0.6", "@types/qrcode": "^1.5.5", "autoprefixer": "^10.4.20", "bits-ui": "1.0.0-next.74", @@ -63,6 +66,7 @@ "tailwind-variants": "^0.3.0", "tailwindcss": "^3.4.9", "tailwindcss-animate": "^1.0.7", + "tsx": "^4.19.2", "typescript": "^5.0.0", "vite": "^6.0.6", "vitest": "^2.0.4", @@ -88,6 +92,7 @@ "arctic": "^2.3.3", "argon2": "^0.41.1", "dayjs": "^1.11.13", + "dotenv": "^16.4.7", "drizzle-orm": "^0.38.3", "drizzle-zod": "^0.6.1", "hono": "^4.6.14", @@ -99,6 +104,7 @@ "mode-watcher": "^0.5.0", "nanoid": "^5.0.9", "pg": "^8.13.1", + "pg-pool": "^3.7.0", "pino": "^9.6.0", "pino-pretty": "^13.0.0", "postgres": "^3.4.4", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 13227e8..286fed4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -19,7 +19,7 @@ importers: version: 0.4.2(hono@4.6.14)(zod@3.24.1) '@inlang/paraglide-sveltekit': specifier: ^0.15.0 - version: 0.15.0(@sveltejs/kit@2.15.0(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(yaml@2.6.1)))(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(yaml@2.6.1))) + version: 0.15.0(@sveltejs/kit@2.15.0(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)))(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1))) '@needle-di/core': specifier: ^0.8.4 version: 0.8.4 @@ -65,6 +65,9 @@ importers: dayjs: specifier: ^1.11.13 version: 1.11.13 + dotenv: + specifier: ^16.4.7 + version: 16.4.7 drizzle-orm: specifier: ^0.38.3 version: 0.38.3(@types/pg@8.11.10)(@types/react@19.0.2)(pg@8.13.1)(postgres@3.4.5)(react@18.3.1) @@ -98,6 +101,9 @@ importers: pg: specifier: ^8.13.1 version: 8.13.1 + pg-pool: + specifier: ^3.7.0 + version: 3.7.0(pg@8.13.1) pino: specifier: ^9.6.0 version: 9.6.0 @@ -146,7 +152,7 @@ importers: version: 8.4.7(storybook@8.4.7) '@storybook/addon-svelte-csf': specifier: ^5.0.0-next.21 - version: 5.0.0-next.21(@storybook/svelte@8.4.7(storybook@8.4.7)(svelte@5.16.0))(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(yaml@2.6.1)))(storybook@8.4.7)(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(yaml@2.6.1)) + version: 5.0.0-next.21(@storybook/svelte@8.4.7(storybook@8.4.7)(svelte@5.16.0))(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)))(storybook@8.4.7)(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)) '@storybook/blocks': specifier: ^8.4.7 version: 8.4.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@8.4.7) @@ -155,22 +161,22 @@ importers: version: 8.4.7(storybook@8.4.7)(svelte@5.16.0) '@storybook/sveltekit': specifier: ^8.4.7 - version: 8.4.7(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(yaml@2.6.1)))(postcss-load-config@4.0.2(postcss@8.4.49))(postcss@8.4.49)(storybook@8.4.7)(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(yaml@2.6.1)) + version: 8.4.7(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)))(postcss-load-config@4.0.2(postcss@8.4.49))(postcss@8.4.49)(storybook@8.4.7)(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)) '@storybook/test': specifier: ^8.4.7 version: 8.4.7(storybook@8.4.7) '@sveltejs/adapter-node': specifier: ^5.2.9 - version: 5.2.11(@sveltejs/kit@2.15.0(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(yaml@2.6.1)))(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(yaml@2.6.1))) + version: 5.2.11(@sveltejs/kit@2.15.0(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)))(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1))) '@sveltejs/enhanced-img': specifier: ^0.4.4 - version: 0.4.4(rollup@4.29.1)(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(yaml@2.6.1)) + version: 0.4.4(rollup@4.29.1)(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)) '@sveltejs/kit': specifier: ^2.9.0 - version: 2.15.0(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(yaml@2.6.1)))(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(yaml@2.6.1)) + version: 2.15.0(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)))(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)) '@sveltejs/vite-plugin-svelte': specifier: ^5.0.3 - version: 5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(yaml@2.6.1)) + version: 5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)) '@tanstack/svelte-query': specifier: ^5.62.9 version: 5.62.9(svelte@5.16.0) @@ -186,6 +192,9 @@ importers: '@types/pg': specifier: ^8.11.10 version: 8.11.10 + '@types/pg-pool': + specifier: ^2.0.6 + version: 2.0.6 '@types/qrcode': specifier: ^1.5.5 version: 1.5.5 @@ -203,7 +212,7 @@ importers: version: 0.30.1 formsnap: specifier: ^2.0.0 - version: 2.0.0(svelte@5.16.0)(sveltekit-superforms@2.22.1(@sveltejs/kit@2.15.0(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(yaml@2.6.1)))(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(yaml@2.6.1)))(@types/json-schema@7.0.15)(svelte@5.16.0)(typescript@5.7.2)) + version: 2.0.0(svelte@5.16.0)(sveltekit-superforms@2.22.1(@sveltejs/kit@2.15.0(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)))(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)))(@types/json-schema@7.0.15)(svelte@5.16.0)(typescript@5.7.2)) lucide-svelte: specifier: ^0.469.0 version: 0.469.0(svelte@5.16.0) @@ -230,10 +239,10 @@ importers: version: 0.3.28(svelte@5.16.0) sveltekit-flash-message: specifier: ^2.4.4 - version: 2.4.4(@sveltejs/kit@2.15.0(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(yaml@2.6.1)))(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(yaml@2.6.1)))(svelte@5.16.0) + version: 2.4.4(@sveltejs/kit@2.15.0(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)))(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)))(svelte@5.16.0) sveltekit-superforms: specifier: ^2.22.1 - version: 2.22.1(@sveltejs/kit@2.15.0(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(yaml@2.6.1)))(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(yaml@2.6.1)))(@types/json-schema@7.0.15)(svelte@5.16.0)(typescript@5.7.2) + version: 2.22.1(@sveltejs/kit@2.15.0(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)))(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)))(@types/json-schema@7.0.15)(svelte@5.16.0)(typescript@5.7.2) tailwind-merge: specifier: ^2.6.0 version: 2.6.0 @@ -246,12 +255,15 @@ importers: tailwindcss-animate: specifier: ^1.0.7 version: 1.0.7(tailwindcss@3.4.17) + tsx: + specifier: ^4.19.2 + version: 4.19.2 typescript: specifier: ^5.0.0 version: 5.7.2 vite: specifier: ^6.0.6 - version: 6.0.6(@types/node@22.10.2)(jiti@1.21.7)(yaml@2.6.1) + version: 6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1) vitest: specifier: ^2.0.4 version: 2.1.8(@types/node@22.10.2) @@ -574,6 +586,12 @@ packages: cpu: [ppc64] os: [aix] + '@esbuild/aix-ppc64@0.23.1': + resolution: {integrity: sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + '@esbuild/aix-ppc64@0.24.2': resolution: {integrity: sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA==} engines: {node: '>=18'} @@ -598,6 +616,12 @@ packages: cpu: [arm64] os: [android] + '@esbuild/android-arm64@0.23.1': + resolution: {integrity: sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + '@esbuild/android-arm64@0.24.2': resolution: {integrity: sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg==} engines: {node: '>=18'} @@ -622,6 +646,12 @@ packages: cpu: [arm] os: [android] + '@esbuild/android-arm@0.23.1': + resolution: {integrity: sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + '@esbuild/android-arm@0.24.2': resolution: {integrity: sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q==} engines: {node: '>=18'} @@ -646,6 +676,12 @@ packages: cpu: [x64] os: [android] + '@esbuild/android-x64@0.23.1': + resolution: {integrity: sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + '@esbuild/android-x64@0.24.2': resolution: {integrity: sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw==} engines: {node: '>=18'} @@ -670,6 +706,12 @@ packages: cpu: [arm64] os: [darwin] + '@esbuild/darwin-arm64@0.23.1': + resolution: {integrity: sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + '@esbuild/darwin-arm64@0.24.2': resolution: {integrity: sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA==} engines: {node: '>=18'} @@ -694,6 +736,12 @@ packages: cpu: [x64] os: [darwin] + '@esbuild/darwin-x64@0.23.1': + resolution: {integrity: sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + '@esbuild/darwin-x64@0.24.2': resolution: {integrity: sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA==} engines: {node: '>=18'} @@ -718,6 +766,12 @@ packages: cpu: [arm64] os: [freebsd] + '@esbuild/freebsd-arm64@0.23.1': + resolution: {integrity: sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + '@esbuild/freebsd-arm64@0.24.2': resolution: {integrity: sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg==} engines: {node: '>=18'} @@ -742,6 +796,12 @@ packages: cpu: [x64] os: [freebsd] + '@esbuild/freebsd-x64@0.23.1': + resolution: {integrity: sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + '@esbuild/freebsd-x64@0.24.2': resolution: {integrity: sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q==} engines: {node: '>=18'} @@ -766,6 +826,12 @@ packages: cpu: [arm64] os: [linux] + '@esbuild/linux-arm64@0.23.1': + resolution: {integrity: sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + '@esbuild/linux-arm64@0.24.2': resolution: {integrity: sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg==} engines: {node: '>=18'} @@ -790,6 +856,12 @@ packages: cpu: [arm] os: [linux] + '@esbuild/linux-arm@0.23.1': + resolution: {integrity: sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + '@esbuild/linux-arm@0.24.2': resolution: {integrity: sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA==} engines: {node: '>=18'} @@ -814,6 +886,12 @@ packages: cpu: [ia32] os: [linux] + '@esbuild/linux-ia32@0.23.1': + resolution: {integrity: sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + '@esbuild/linux-ia32@0.24.2': resolution: {integrity: sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw==} engines: {node: '>=18'} @@ -838,6 +916,12 @@ packages: cpu: [loong64] os: [linux] + '@esbuild/linux-loong64@0.23.1': + resolution: {integrity: sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + '@esbuild/linux-loong64@0.24.2': resolution: {integrity: sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ==} engines: {node: '>=18'} @@ -862,6 +946,12 @@ packages: cpu: [mips64el] os: [linux] + '@esbuild/linux-mips64el@0.23.1': + resolution: {integrity: sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + '@esbuild/linux-mips64el@0.24.2': resolution: {integrity: sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw==} engines: {node: '>=18'} @@ -886,6 +976,12 @@ packages: cpu: [ppc64] os: [linux] + '@esbuild/linux-ppc64@0.23.1': + resolution: {integrity: sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + '@esbuild/linux-ppc64@0.24.2': resolution: {integrity: sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw==} engines: {node: '>=18'} @@ -910,6 +1006,12 @@ packages: cpu: [riscv64] os: [linux] + '@esbuild/linux-riscv64@0.23.1': + resolution: {integrity: sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + '@esbuild/linux-riscv64@0.24.2': resolution: {integrity: sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q==} engines: {node: '>=18'} @@ -934,6 +1036,12 @@ packages: cpu: [s390x] os: [linux] + '@esbuild/linux-s390x@0.23.1': + resolution: {integrity: sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + '@esbuild/linux-s390x@0.24.2': resolution: {integrity: sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw==} engines: {node: '>=18'} @@ -958,6 +1066,12 @@ packages: cpu: [x64] os: [linux] + '@esbuild/linux-x64@0.23.1': + resolution: {integrity: sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + '@esbuild/linux-x64@0.24.2': resolution: {integrity: sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q==} engines: {node: '>=18'} @@ -988,12 +1102,24 @@ packages: cpu: [x64] os: [netbsd] + '@esbuild/netbsd-x64@0.23.1': + resolution: {integrity: sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + '@esbuild/netbsd-x64@0.24.2': resolution: {integrity: sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] + '@esbuild/openbsd-arm64@0.23.1': + resolution: {integrity: sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + '@esbuild/openbsd-arm64@0.24.2': resolution: {integrity: sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A==} engines: {node: '>=18'} @@ -1018,6 +1144,12 @@ packages: cpu: [x64] os: [openbsd] + '@esbuild/openbsd-x64@0.23.1': + resolution: {integrity: sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + '@esbuild/openbsd-x64@0.24.2': resolution: {integrity: sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA==} engines: {node: '>=18'} @@ -1042,6 +1174,12 @@ packages: cpu: [x64] os: [sunos] + '@esbuild/sunos-x64@0.23.1': + resolution: {integrity: sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + '@esbuild/sunos-x64@0.24.2': resolution: {integrity: sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig==} engines: {node: '>=18'} @@ -1066,6 +1204,12 @@ packages: cpu: [arm64] os: [win32] + '@esbuild/win32-arm64@0.23.1': + resolution: {integrity: sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + '@esbuild/win32-arm64@0.24.2': resolution: {integrity: sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ==} engines: {node: '>=18'} @@ -1090,6 +1234,12 @@ packages: cpu: [ia32] os: [win32] + '@esbuild/win32-ia32@0.23.1': + resolution: {integrity: sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + '@esbuild/win32-ia32@0.24.2': resolution: {integrity: sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA==} engines: {node: '>=18'} @@ -1114,6 +1264,12 @@ packages: cpu: [x64] os: [win32] + '@esbuild/win32-x64@0.23.1': + resolution: {integrity: sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + '@esbuild/win32-x64@0.24.2': resolution: {integrity: sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg==} engines: {node: '>=18'} @@ -2111,6 +2267,9 @@ packages: '@types/node@22.10.2': resolution: {integrity: sha512-Xxr6BBRCAOQixvonOye19wnzyDiUtTeqldOOmj3CkeblonbccA12PFwlufvRdrpjXxqnmUaeiU5EOA+7s5diUQ==} + '@types/pg-pool@2.0.6': + resolution: {integrity: sha512-TaAUE5rq2VQYxab5Ts7WZhKNmuN78Q6PiFonTDdpbx8a1H0M1vhy3rhiMjl+e2iHmogyMw7jZF4FrE6eJUy5HQ==} + '@types/pg@8.11.10': resolution: {integrity: sha512-LczQUW4dbOQzsH2RQ5qoeJ6qJPdrcM/DcMLoqWQkMLMsq83J5lAX3LXjdkWdpscFy67JSOWDnh7Ny/sPFykmkg==} @@ -2721,6 +2880,10 @@ packages: domutils@2.8.0: resolution: {integrity: sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==} + dotenv@16.4.7: + resolution: {integrity: sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==} + engines: {node: '>=12'} + drizzle-kit@0.30.1: resolution: {integrity: sha512-HmA/NeewvHywhJ2ENXD3KvOuM/+K2dGLJfxVfIHsGwaqKICJnS+Ke2L6UcSrSrtMJLJaT0Im1Qv4TFXfaZShyw==} hasBin: true @@ -2913,6 +3076,11 @@ packages: engines: {node: '>=12'} hasBin: true + esbuild@0.23.1: + resolution: {integrity: sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==} + engines: {node: '>=18'} + hasBin: true + esbuild@0.24.2: resolution: {integrity: sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==} engines: {node: '>=18'} @@ -4592,6 +4760,11 @@ packages: 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 + tween-functions@1.2.0: resolution: {integrity: sha512-PZBtLYcCLtEcjL14Fzb1gSxPBeL7nWvGhO5ZFPGqziCcr8uvHp0NDmdjBchp6KHL+tExcg0m3NISmKxhU394dA==} @@ -5159,6 +5332,9 @@ snapshots: '@esbuild/aix-ppc64@0.21.5': optional: true + '@esbuild/aix-ppc64@0.23.1': + optional: true + '@esbuild/aix-ppc64@0.24.2': optional: true @@ -5171,6 +5347,9 @@ snapshots: '@esbuild/android-arm64@0.21.5': optional: true + '@esbuild/android-arm64@0.23.1': + optional: true + '@esbuild/android-arm64@0.24.2': optional: true @@ -5183,6 +5362,9 @@ snapshots: '@esbuild/android-arm@0.21.5': optional: true + '@esbuild/android-arm@0.23.1': + optional: true + '@esbuild/android-arm@0.24.2': optional: true @@ -5195,6 +5377,9 @@ snapshots: '@esbuild/android-x64@0.21.5': optional: true + '@esbuild/android-x64@0.23.1': + optional: true + '@esbuild/android-x64@0.24.2': optional: true @@ -5207,6 +5392,9 @@ snapshots: '@esbuild/darwin-arm64@0.21.5': optional: true + '@esbuild/darwin-arm64@0.23.1': + optional: true + '@esbuild/darwin-arm64@0.24.2': optional: true @@ -5219,6 +5407,9 @@ snapshots: '@esbuild/darwin-x64@0.21.5': optional: true + '@esbuild/darwin-x64@0.23.1': + optional: true + '@esbuild/darwin-x64@0.24.2': optional: true @@ -5231,6 +5422,9 @@ snapshots: '@esbuild/freebsd-arm64@0.21.5': optional: true + '@esbuild/freebsd-arm64@0.23.1': + optional: true + '@esbuild/freebsd-arm64@0.24.2': optional: true @@ -5243,6 +5437,9 @@ snapshots: '@esbuild/freebsd-x64@0.21.5': optional: true + '@esbuild/freebsd-x64@0.23.1': + optional: true + '@esbuild/freebsd-x64@0.24.2': optional: true @@ -5255,6 +5452,9 @@ snapshots: '@esbuild/linux-arm64@0.21.5': optional: true + '@esbuild/linux-arm64@0.23.1': + optional: true + '@esbuild/linux-arm64@0.24.2': optional: true @@ -5267,6 +5467,9 @@ snapshots: '@esbuild/linux-arm@0.21.5': optional: true + '@esbuild/linux-arm@0.23.1': + optional: true + '@esbuild/linux-arm@0.24.2': optional: true @@ -5279,6 +5482,9 @@ snapshots: '@esbuild/linux-ia32@0.21.5': optional: true + '@esbuild/linux-ia32@0.23.1': + optional: true + '@esbuild/linux-ia32@0.24.2': optional: true @@ -5291,6 +5497,9 @@ snapshots: '@esbuild/linux-loong64@0.21.5': optional: true + '@esbuild/linux-loong64@0.23.1': + optional: true + '@esbuild/linux-loong64@0.24.2': optional: true @@ -5303,6 +5512,9 @@ snapshots: '@esbuild/linux-mips64el@0.21.5': optional: true + '@esbuild/linux-mips64el@0.23.1': + optional: true + '@esbuild/linux-mips64el@0.24.2': optional: true @@ -5315,6 +5527,9 @@ snapshots: '@esbuild/linux-ppc64@0.21.5': optional: true + '@esbuild/linux-ppc64@0.23.1': + optional: true + '@esbuild/linux-ppc64@0.24.2': optional: true @@ -5327,6 +5542,9 @@ snapshots: '@esbuild/linux-riscv64@0.21.5': optional: true + '@esbuild/linux-riscv64@0.23.1': + optional: true + '@esbuild/linux-riscv64@0.24.2': optional: true @@ -5339,6 +5557,9 @@ snapshots: '@esbuild/linux-s390x@0.21.5': optional: true + '@esbuild/linux-s390x@0.23.1': + optional: true + '@esbuild/linux-s390x@0.24.2': optional: true @@ -5351,6 +5572,9 @@ snapshots: '@esbuild/linux-x64@0.21.5': optional: true + '@esbuild/linux-x64@0.23.1': + optional: true + '@esbuild/linux-x64@0.24.2': optional: true @@ -5366,9 +5590,15 @@ snapshots: '@esbuild/netbsd-x64@0.21.5': optional: true + '@esbuild/netbsd-x64@0.23.1': + optional: true + '@esbuild/netbsd-x64@0.24.2': optional: true + '@esbuild/openbsd-arm64@0.23.1': + optional: true + '@esbuild/openbsd-arm64@0.24.2': optional: true @@ -5381,6 +5611,9 @@ snapshots: '@esbuild/openbsd-x64@0.21.5': optional: true + '@esbuild/openbsd-x64@0.23.1': + optional: true + '@esbuild/openbsd-x64@0.24.2': optional: true @@ -5393,6 +5626,9 @@ snapshots: '@esbuild/sunos-x64@0.21.5': optional: true + '@esbuild/sunos-x64@0.23.1': + optional: true + '@esbuild/sunos-x64@0.24.2': optional: true @@ -5405,6 +5641,9 @@ snapshots: '@esbuild/win32-arm64@0.21.5': optional: true + '@esbuild/win32-arm64@0.23.1': + optional: true + '@esbuild/win32-arm64@0.24.2': optional: true @@ -5417,6 +5656,9 @@ snapshots: '@esbuild/win32-ia32@0.21.5': optional: true + '@esbuild/win32-ia32@0.23.1': + optional: true + '@esbuild/win32-ia32@0.24.2': optional: true @@ -5429,6 +5671,9 @@ snapshots: '@esbuild/win32-x64@0.21.5': optional: true + '@esbuild/win32-x64@0.23.1': + optional: true + '@esbuild/win32-x64@0.24.2': optional: true @@ -5624,12 +5869,12 @@ snapshots: - babel-plugin-macros - debug - '@inlang/paraglide-sveltekit@0.15.0(@sveltejs/kit@2.15.0(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(yaml@2.6.1)))(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(yaml@2.6.1)))': + '@inlang/paraglide-sveltekit@0.15.0(@sveltejs/kit@2.15.0(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)))(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)))': dependencies: '@inlang/paraglide-js': 1.11.3 '@inlang/paraglide-vite': 1.3.0 '@lix-js/client': 2.2.1 - '@sveltejs/kit': 2.15.0(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(yaml@2.6.1)))(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(yaml@2.6.1)) + '@sveltejs/kit': 2.15.0(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)))(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)) commander: 12.1.0 dedent: 1.5.1 devalue: 4.3.3 @@ -6249,21 +6494,21 @@ snapshots: storybook: 8.4.7 ts-dedent: 2.2.0 - '@storybook/addon-svelte-csf@5.0.0-next.21(@storybook/svelte@8.4.7(storybook@8.4.7)(svelte@5.16.0))(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(yaml@2.6.1)))(storybook@8.4.7)(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(yaml@2.6.1))': + '@storybook/addon-svelte-csf@5.0.0-next.21(@storybook/svelte@8.4.7(storybook@8.4.7)(svelte@5.16.0))(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)))(storybook@8.4.7)(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1))': dependencies: '@storybook/csf': 0.1.12 '@storybook/docs-tools': 8.4.7(storybook@8.4.7) '@storybook/node-logger': 8.4.7(storybook@8.4.7) '@storybook/svelte': 8.4.7(storybook@8.4.7)(svelte@5.16.0) '@storybook/types': 8.4.7(storybook@8.4.7) - '@sveltejs/vite-plugin-svelte': 5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(yaml@2.6.1)) + '@sveltejs/vite-plugin-svelte': 5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)) dedent: 1.5.3 es-toolkit: 1.30.1 esrap: 1.3.2 magic-string: 0.30.17 svelte: 5.16.0 svelte-ast-print: 0.4.2(svelte@5.16.0) - vite: 6.0.6(@types/node@22.10.2)(jiti@1.21.7)(yaml@2.6.1) + vite: 6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1) zimmerframe: 1.1.2 transitivePeerDependencies: - babel-plugin-macros @@ -6288,13 +6533,13 @@ snapshots: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - '@storybook/builder-vite@8.4.7(storybook@8.4.7)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(yaml@2.6.1))': + '@storybook/builder-vite@8.4.7(storybook@8.4.7)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1))': dependencies: '@storybook/csf-plugin': 8.4.7(storybook@8.4.7) browser-assert: 1.2.1 storybook: 8.4.7 ts-dedent: 2.2.0 - vite: 6.0.6(@types/node@22.10.2)(jiti@1.21.7)(yaml@2.6.1) + vite: 6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1) '@storybook/components@8.4.7(storybook@8.4.7)': dependencies: @@ -6362,11 +6607,11 @@ snapshots: react-dom: 18.3.1(react@18.3.1) storybook: 8.4.7 - '@storybook/svelte-vite@8.4.7(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(yaml@2.6.1)))(postcss-load-config@4.0.2(postcss@8.4.49))(postcss@8.4.49)(storybook@8.4.7)(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(yaml@2.6.1))': + '@storybook/svelte-vite@8.4.7(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)))(postcss-load-config@4.0.2(postcss@8.4.49))(postcss@8.4.49)(storybook@8.4.7)(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1))': dependencies: - '@storybook/builder-vite': 8.4.7(storybook@8.4.7)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(yaml@2.6.1)) + '@storybook/builder-vite': 8.4.7(storybook@8.4.7)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)) '@storybook/svelte': 8.4.7(storybook@8.4.7)(svelte@5.16.0) - '@sveltejs/vite-plugin-svelte': 5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(yaml@2.6.1)) + '@sveltejs/vite-plugin-svelte': 5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)) magic-string: 0.30.17 storybook: 8.4.7 svelte: 5.16.0 @@ -6375,7 +6620,7 @@ snapshots: sveltedoc-parser: 4.2.1 ts-dedent: 2.2.0 typescript: 5.7.2 - vite: 6.0.6(@types/node@22.10.2)(jiti@1.21.7)(yaml@2.6.1) + vite: 6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1) transitivePeerDependencies: - '@babel/core' - coffeescript @@ -6403,15 +6648,15 @@ snapshots: transitivePeerDependencies: - supports-color - '@storybook/sveltekit@8.4.7(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(yaml@2.6.1)))(postcss-load-config@4.0.2(postcss@8.4.49))(postcss@8.4.49)(storybook@8.4.7)(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(yaml@2.6.1))': + '@storybook/sveltekit@8.4.7(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)))(postcss-load-config@4.0.2(postcss@8.4.49))(postcss@8.4.49)(storybook@8.4.7)(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1))': dependencies: '@storybook/addon-actions': 8.4.7(storybook@8.4.7) - '@storybook/builder-vite': 8.4.7(storybook@8.4.7)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(yaml@2.6.1)) + '@storybook/builder-vite': 8.4.7(storybook@8.4.7)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)) '@storybook/svelte': 8.4.7(storybook@8.4.7)(svelte@5.16.0) - '@storybook/svelte-vite': 8.4.7(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(yaml@2.6.1)))(postcss-load-config@4.0.2(postcss@8.4.49))(postcss@8.4.49)(storybook@8.4.7)(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(yaml@2.6.1)) + '@storybook/svelte-vite': 8.4.7(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)))(postcss-load-config@4.0.2(postcss@8.4.49))(postcss@8.4.49)(storybook@8.4.7)(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)) storybook: 8.4.7 svelte: 5.16.0 - vite: 6.0.6(@types/node@22.10.2)(jiti@1.21.7)(yaml@2.6.1) + vite: 6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1) transitivePeerDependencies: - '@babel/core' - '@sveltejs/vite-plugin-svelte' @@ -6445,29 +6690,29 @@ snapshots: dependencies: storybook: 8.4.7 - '@sveltejs/adapter-node@5.2.11(@sveltejs/kit@2.15.0(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(yaml@2.6.1)))(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(yaml@2.6.1)))': + '@sveltejs/adapter-node@5.2.11(@sveltejs/kit@2.15.0(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)))(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)))': dependencies: '@rollup/plugin-commonjs': 28.0.2(rollup@4.29.1) '@rollup/plugin-json': 6.1.0(rollup@4.29.1) '@rollup/plugin-node-resolve': 16.0.0(rollup@4.29.1) - '@sveltejs/kit': 2.15.0(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(yaml@2.6.1)))(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(yaml@2.6.1)) + '@sveltejs/kit': 2.15.0(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)))(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)) rollup: 4.29.1 - '@sveltejs/enhanced-img@0.4.4(rollup@4.29.1)(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(yaml@2.6.1))': + '@sveltejs/enhanced-img@0.4.4(rollup@4.29.1)(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1))': dependencies: magic-string: 0.30.17 sharp: 0.33.5 svelte: 5.16.0 svelte-parse-markup: 0.1.5(svelte@5.16.0) - vite: 6.0.6(@types/node@22.10.2)(jiti@1.21.7)(yaml@2.6.1) + vite: 6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1) vite-imagetools: 7.0.5(rollup@4.29.1) zimmerframe: 1.1.2 transitivePeerDependencies: - rollup - '@sveltejs/kit@2.15.0(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(yaml@2.6.1)))(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(yaml@2.6.1))': + '@sveltejs/kit@2.15.0(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)))(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1))': dependencies: - '@sveltejs/vite-plugin-svelte': 5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(yaml@2.6.1)) + '@sveltejs/vite-plugin-svelte': 5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)) '@types/cookie': 0.6.0 cookie: 0.6.0 devalue: 5.1.1 @@ -6481,27 +6726,27 @@ snapshots: sirv: 3.0.0 svelte: 5.16.0 tiny-glob: 0.2.9 - vite: 6.0.6(@types/node@22.10.2)(jiti@1.21.7)(yaml@2.6.1) + vite: 6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1) - '@sveltejs/vite-plugin-svelte-inspector@4.0.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(yaml@2.6.1)))(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(yaml@2.6.1))': + '@sveltejs/vite-plugin-svelte-inspector@4.0.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)))(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1))': dependencies: - '@sveltejs/vite-plugin-svelte': 5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(yaml@2.6.1)) + '@sveltejs/vite-plugin-svelte': 5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)) debug: 4.4.0 svelte: 5.16.0 - vite: 6.0.6(@types/node@22.10.2)(jiti@1.21.7)(yaml@2.6.1) + vite: 6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1) transitivePeerDependencies: - supports-color - '@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(yaml@2.6.1))': + '@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1))': dependencies: - '@sveltejs/vite-plugin-svelte-inspector': 4.0.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(yaml@2.6.1)))(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(yaml@2.6.1)) + '@sveltejs/vite-plugin-svelte-inspector': 4.0.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)))(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)) debug: 4.4.0 deepmerge: 4.3.1 kleur: 4.1.5 magic-string: 0.30.17 svelte: 5.16.0 - vite: 6.0.6(@types/node@22.10.2)(jiti@1.21.7)(yaml@2.6.1) - vitefu: 1.0.4(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(yaml@2.6.1)) + vite: 6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1) + vitefu: 1.0.4(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)) transitivePeerDependencies: - supports-color @@ -6595,6 +6840,10 @@ snapshots: dependencies: undici-types: 6.20.0 + '@types/pg-pool@2.0.6': + dependencies: + '@types/pg': 8.11.10 + '@types/pg@8.11.10': dependencies: '@types/node': 22.10.2 @@ -7162,6 +7411,8 @@ snapshots: domelementtype: 2.3.0 domhandler: 4.3.1 + dotenv@16.4.7: {} + drizzle-kit@0.30.1: dependencies: '@drizzle-team/brocli': 0.10.2 @@ -7336,6 +7587,33 @@ snapshots: '@esbuild/win32-ia32': 0.21.5 '@esbuild/win32-x64': 0.21.5 + esbuild@0.23.1: + optionalDependencies: + '@esbuild/aix-ppc64': 0.23.1 + '@esbuild/android-arm': 0.23.1 + '@esbuild/android-arm64': 0.23.1 + '@esbuild/android-x64': 0.23.1 + '@esbuild/darwin-arm64': 0.23.1 + '@esbuild/darwin-x64': 0.23.1 + '@esbuild/freebsd-arm64': 0.23.1 + '@esbuild/freebsd-x64': 0.23.1 + '@esbuild/linux-arm': 0.23.1 + '@esbuild/linux-arm64': 0.23.1 + '@esbuild/linux-ia32': 0.23.1 + '@esbuild/linux-loong64': 0.23.1 + '@esbuild/linux-mips64el': 0.23.1 + '@esbuild/linux-ppc64': 0.23.1 + '@esbuild/linux-riscv64': 0.23.1 + '@esbuild/linux-s390x': 0.23.1 + '@esbuild/linux-x64': 0.23.1 + '@esbuild/netbsd-x64': 0.23.1 + '@esbuild/openbsd-arm64': 0.23.1 + '@esbuild/openbsd-x64': 0.23.1 + '@esbuild/sunos-x64': 0.23.1 + '@esbuild/win32-arm64': 0.23.1 + '@esbuild/win32-ia32': 0.23.1 + '@esbuild/win32-x64': 0.23.1 + esbuild@0.24.2: optionalDependencies: '@esbuild/aix-ppc64': 0.24.2 @@ -7619,11 +7897,11 @@ snapshots: combined-stream: 1.0.8 mime-types: 2.1.35 - formsnap@2.0.0(svelte@5.16.0)(sveltekit-superforms@2.22.1(@sveltejs/kit@2.15.0(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(yaml@2.6.1)))(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(yaml@2.6.1)))(@types/json-schema@7.0.15)(svelte@5.16.0)(typescript@5.7.2)): + formsnap@2.0.0(svelte@5.16.0)(sveltekit-superforms@2.22.1(@sveltejs/kit@2.15.0(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)))(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)))(@types/json-schema@7.0.15)(svelte@5.16.0)(typescript@5.7.2)): dependencies: svelte: 5.16.0 svelte-toolbelt: 0.5.0(svelte@5.16.0) - sveltekit-superforms: 2.22.1(@sveltejs/kit@2.15.0(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(yaml@2.6.1)))(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(yaml@2.6.1)))(@types/json-schema@7.0.15)(svelte@5.16.0)(typescript@5.7.2) + sveltekit-superforms: 2.22.1(@sveltejs/kit@2.15.0(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)))(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)))(@types/json-schema@7.0.15)(svelte@5.16.0)(typescript@5.7.2) forwarded@0.2.0: {} @@ -8945,14 +9223,14 @@ snapshots: transitivePeerDependencies: - supports-color - sveltekit-flash-message@2.4.4(@sveltejs/kit@2.15.0(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(yaml@2.6.1)))(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(yaml@2.6.1)))(svelte@5.16.0): + sveltekit-flash-message@2.4.4(@sveltejs/kit@2.15.0(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)))(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)))(svelte@5.16.0): dependencies: - '@sveltejs/kit': 2.15.0(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(yaml@2.6.1)))(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(yaml@2.6.1)) + '@sveltejs/kit': 2.15.0(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)))(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)) svelte: 5.16.0 - sveltekit-superforms@2.22.1(@sveltejs/kit@2.15.0(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(yaml@2.6.1)))(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(yaml@2.6.1)))(@types/json-schema@7.0.15)(svelte@5.16.0)(typescript@5.7.2): + sveltekit-superforms@2.22.1(@sveltejs/kit@2.15.0(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)))(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)))(@types/json-schema@7.0.15)(svelte@5.16.0)(typescript@5.7.2): dependencies: - '@sveltejs/kit': 2.15.0(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(yaml@2.6.1)))(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(yaml@2.6.1)) + '@sveltejs/kit': 2.15.0(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)))(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)) devalue: 5.1.1 memoize-weak: 1.0.2 svelte: 5.16.0 @@ -9088,6 +9366,13 @@ snapshots: tslib@2.8.1: {} + tsx@4.19.2: + dependencies: + esbuild: 0.23.1 + get-tsconfig: 4.8.1 + optionalDependencies: + fsevents: 2.3.3 + tween-functions@1.2.0: {} type-check@0.4.0: @@ -9199,7 +9484,7 @@ snapshots: '@types/node': 22.10.2 fsevents: 2.3.3 - vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(yaml@2.6.1): + vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1): dependencies: esbuild: 0.24.2 postcss: 8.4.49 @@ -9208,11 +9493,12 @@ snapshots: '@types/node': 22.10.2 fsevents: 2.3.3 jiti: 1.21.7 + tsx: 4.19.2 yaml: 2.6.1 - vitefu@1.0.4(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(yaml@2.6.1)): + vitefu@1.0.4(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)): optionalDependencies: - vite: 6.0.6(@types/node@22.10.2)(jiti@1.21.7)(yaml@2.6.1) + vite: 6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1) vitest@2.1.8(@types/node@22.10.2): dependencies: diff --git a/src/lib/server/api/common/configs/config.service.ts b/src/lib/server/api/common/configs/config.service.ts index 738367e..58f9426 100644 --- a/src/lib/server/api/common/configs/config.service.ts +++ b/src/lib/server/api/common/configs/config.service.ts @@ -5,27 +5,31 @@ import * as envs from '$env/static/private'; @injectable() export class ConfigService { - envs: EnvsDto; + envs: EnvsDto; - constructor() { - this.envs = this.parseEnvs()!; - } + constructor() { + const parsedEnvs = this.parseEnvs(); + if (parsedEnvs === null || parsedEnvs === undefined) { + throw new Error('Failed to parse environment variables'); + } + this.envs = parsedEnvs; + } - private parseEnvs() { - return envsDto.parse(envs); - } + private parseEnvs() { + return envsDto.parse(envs); + } - validateEnvs() { - try { - return envsDto.parse(envs); - } catch (err) { - if (err instanceof z.ZodError) { - const { fieldErrors } = err.flatten(); - const errorMessage = Object.entries(fieldErrors) - .map(([field, errors]) => (errors ? `${field}: ${errors.join(', ')}` : field)) - .join('\n '); - throw new Error(`Missing environment variables:\n ${errorMessage}`); - } - } - } + validateEnvs() { + try { + return envsDto.parse(envs); + } catch (err) { + if (err instanceof z.ZodError) { + const { fieldErrors } = err.flatten(); + const errorMessage = Object.entries(fieldErrors) + .map(([field, errors]) => (errors ? `${field}: ${errors.join(', ')}` : field)) + .join('\n '); + throw new Error(`Missing environment variables:\n ${errorMessage}`); + } + } + } } diff --git a/src/lib/server/api/databases/postgres/drizzle-schema.ts b/src/lib/server/api/databases/postgres/drizzle-schema.ts index 3157b16..b1b6b2c 100644 --- a/src/lib/server/api/databases/postgres/drizzle-schema.ts +++ b/src/lib/server/api/databases/postgres/drizzle-schema.ts @@ -1,3 +1,6 @@ +export * from '../../users/tables/credentials.table'; export * from '../../roles/tables/roles.table'; export * from '../../users/tables/user-roles.table'; export * from '../../users/tables/users.table'; +export * from '../../mfa/tables/two-factor.table'; +export * from '../../mfa/tables/recovery-codes.table'; diff --git a/src/lib/server/api/databases/postgres/drizzle.service.ts b/src/lib/server/api/databases/postgres/drizzle.service.ts index a0abecc..3e63809 100644 --- a/src/lib/server/api/databases/postgres/drizzle.service.ts +++ b/src/lib/server/api/databases/postgres/drizzle.service.ts @@ -1,4 +1,4 @@ -import pg from 'pg'; +import Pool from 'pg-pool'; import * as drizzleSchema from './drizzle-schema'; import { inject, injectable } from '@needle-di/core'; import { drizzle, type NodePgDatabase } from 'drizzle-orm/node-postgres'; @@ -6,25 +6,24 @@ import { ConfigService } from '../../common/configs/config.service'; @injectable() export class DrizzleService { - public pool: pg.Pool; - public db: NodePgDatabase; - public schema: typeof drizzleSchema = drizzleSchema; - constructor(private configService = inject(ConfigService)) { - const pool = new pg.Pool({ - user: this.configService.envs.DATABASE_USER, - password: this.configService.envs.DATABASE_PASSWORD, - host: this.configService.envs.DATABASE_HOST, - port: Number(this.configService.envs.DATABASE_PORT).valueOf(), - database: this.configService.envs.DATABASE_DB, - ssl: false, - max: this.configService.envs.DB_MIGRATING || this.configService.envs.DB_SEEDING ? 1 : undefined, - }); - this.pool = pool; - this.db = drizzle({ - client: pool, - casing: 'snake_case', - schema: drizzleSchema, - logger: this.configService.envs.ENV !== 'prod', - }); - } + public db: NodePgDatabase; + public schema: typeof drizzleSchema = drizzleSchema; + constructor(private configService = inject(ConfigService)) { + this.db = drizzle( + new Pool({ + user: this.configService.envs.DATABASE_USER, + password: this.configService.envs.DATABASE_PASSWORD, + host: this.configService.envs.DATABASE_HOST, + port: Number(this.configService.envs.DATABASE_PORT).valueOf(), + database: this.configService.envs.DATABASE_DB, + ssl: false, + max: this.configService.envs.DB_MIGRATING || this.configService.envs.DB_SEEDING ? 1 : undefined, + }), + { + casing: 'snake_case', + schema: drizzleSchema, + logger: this.configService.envs.ENV !== 'prod', + }, + ); + } } 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..564a96a --- /dev/null +++ b/src/lib/server/api/databases/postgres/migrate.ts @@ -0,0 +1,37 @@ +import 'dotenv/config'; +import { migrate } from 'drizzle-orm/node-postgres/migrator'; +import * as drizzleSchema from './drizzle-schema'; +import config from '../../../../../../drizzle.config'; +import Pool from 'pg-pool'; +import { drizzle } from 'drizzle-orm/node-postgres'; + +if (!config.out) { + console.error('No migrations folder specified in drizzle.config.ts'); + process.exit(); +} +if (!process.env.DB_MIGRATING) { + throw new Error('You must set DB_MIGRATING to "true" when running migrations.'); +} + +const db = drizzle( + new Pool({ + user: process.env.DATABASE_USER, + password: process.env.DATABASE_PASSWORD, + host: process.env.DATABASE_HOST, + port: Number(process.env.DATABASE_PORT).valueOf(), + database: process.env.DATABASE_DB, + ssl: false, + max: 1, + }), + { + casing: 'snake_case', + schema: drizzleSchema, + logger: process.env.ENV !== 'prod', + }, +); + +await migrate(db, { migrationsFolder: config.out }); +console.log('Migrations complete'); + +await db.$client.end(); +process.exit(); diff --git a/src/lib/server/api/databases/postgres/seed.ts b/src/lib/server/api/databases/postgres/seed.ts new file mode 100644 index 0000000..a3e95cd --- /dev/null +++ b/src/lib/server/api/databases/postgres/seed.ts @@ -0,0 +1,50 @@ +import 'dotenv/config'; +import { type Table, getTableName, sql } from 'drizzle-orm'; +import { drizzle, type NodePgDatabase } from 'drizzle-orm/node-postgres'; +import * as drizzleSchema from './drizzle-schema'; +import * as seeds from './seeds'; +import * as schema from './drizzle-schema'; +import Pool from 'pg-pool'; + +if (!process.env.DB_SEEDING) { + 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`)); +} + +const db = drizzle( + new Pool({ + user: process.env.DATABASE_USER, + password: process.env.DATABASE_PASSWORD, + host: process.env.DATABASE_HOST, + port: Number(process.env.DATABASE_PORT).valueOf(), + database: process.env.DATABASE_DB, + ssl: false, + max: process.env.DB_MIGRATING || process.env.DB_SEEDING ? 1 : undefined, + }), + { + casing: 'snake_case', + schema: drizzleSchema, + logger: process.env.ENV !== 'prod', + }, +); + +for (const table of [ + schema.credentials_table, + schema.recovery_codes_table, + schema.roles_table, + schema.two_factor_table, + schema.user_roles_table, + schema.users_table, +]) { + // await db.delete(table); // clear tables without truncating / resetting ids + await resetTable(db, table); +} + +await seeds.roles(db); +await seeds.users(db); + +await db.$client.end(); +process.exit(); diff --git a/src/lib/server/api/databases/postgres/seeds/data/roles.json b/src/lib/server/api/databases/postgres/seeds/data/roles.json new file mode 100644 index 0000000..e6747d8 --- /dev/null +++ b/src/lib/server/api/databases/postgres/seeds/data/roles.json @@ -0,0 +1,11 @@ +[ + { + "name": "admin" + }, + { + "name": "user" + }, + { + "name": "moderator" + } +] \ No newline at end of file diff --git a/src/lib/server/api/databases/postgres/seeds/data/users.json b/src/lib/server/api/databases/postgres/seeds/data/users.json new file mode 100644 index 0000000..d649160 --- /dev/null +++ b/src/lib/server/api/databases/postgres/seeds/data/users.json @@ -0,0 +1,45 @@ +[ + { + "first_name": "John", + "last_name": "Smith", + "username": "john.smith", + "email": "john.smith@example.com", + "password": "password", + "roles": [ + { + "name": "user", + "primary": true + } + ] + }, + { + "first_name": "Jane", + "last_name": "Doe", + "username": "jane.doe", + "email": "jane.doe@example.com", + "password": "password", + "roles": [ + { + "name": "user", + "primary": true + } + ] + }, + { + "first_name": "Jane", + "last_name": "Moderator", + "username": "jane.moderator", + "email": "jane.moderator@example.com", + "password": "password", + "roles": [ + { + "name": "moderator", + "primary": true + }, + { + "name": "user", + "primary": false + } + ] + } +] \ No newline at end of file diff --git a/src/lib/server/api/databases/postgres/seeds/index.ts b/src/lib/server/api/databases/postgres/seeds/index.ts new file mode 100644 index 0000000..0a05205 --- /dev/null +++ b/src/lib/server/api/databases/postgres/seeds/index.ts @@ -0,0 +1,2 @@ +export { default as users } from './users'; +export { default as roles } from './roles'; 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..dbfef77 --- /dev/null +++ b/src/lib/server/api/databases/postgres/seeds/roles.ts @@ -0,0 +1,11 @@ +import type { NodePgDatabase } from 'drizzle-orm/node-postgres'; +import * as schema from '../drizzle-schema'; +import roles from './data/roles.json'; + +export default async function seed(db: NodePgDatabase) { + console.log('Creating rolesTable ...'); + for (const role of roles) { + await db.insert(schema.roles_table).values(role).onConflictDoNothing(); + } + console.log('Roles created.'); +} diff --git a/src/lib/server/api/databases/postgres/seeds/users.ts b/src/lib/server/api/databases/postgres/seeds/users.ts new file mode 100644 index 0000000..7f7ed47 --- /dev/null +++ b/src/lib/server/api/databases/postgres/seeds/users.ts @@ -0,0 +1,82 @@ +import { eq } from 'drizzle-orm'; +import type { NodePgDatabase } from 'drizzle-orm/node-postgres'; +import { HashingService } from '../../../common/services/hashing.service'; +import * as schema from '../drizzle-schema'; +import users from './data/users.json'; + +type JsonRole = { + name: string; + primary: boolean; +}; + +export default async function seed(db: NodePgDatabase) { + const hashingService = new HashingService(); + const adminRole = await db.select().from(schema.roles_table).where(eq(schema.roles_table.name, 'admin')); + const userRole = await db.select().from(schema.roles_table).where(eq(schema.roles_table.name, 'user')); + + const adminUser = await db + .insert(schema.users_table) + .values({ + username: `${process.env.ADMIN_USERNAME}`, + email: '', + first_name: 'Brad', + last_name: 'S', + }) + .returning() + .onConflictDoNothing(); + + await db.insert(schema.credentials_table).values({ + user_id: adminUser[0].id, + type: schema.CredentialsType.PASSWORD, + secret_data: await hashingService.hash(`${process.env.ADMIN_PASSWORD}`), + }); + + await db + .insert(schema.user_roles_table) + .values({ + user_id: adminUser[0].id, + role_id: adminRole[0].id, + primary: true, + }) + .onConflictDoNothing(); + + await db + .insert(schema.user_roles_table) + .values({ + user_id: adminUser[0].id, + role_id: userRole[0].id, + primary: false, + }) + .onConflictDoNothing(); + + await Promise.all( + users.map(async (user) => { + const [insertedUser] = await db + .insert(schema.users_table) + .values({ + ...user, + }) + .returning(); + await db.insert(schema.credentials_table).values({ + user_id: insertedUser?.id, + type: schema.CredentialsType.PASSWORD, + secret_data: await hashingService.hash(user.password), + }); + await Promise.all( + user.roles.map(async (role: JsonRole) => { + const foundRole = await db.query.roles_table.findFirst({ + where: eq(schema.roles_table.name, role.name), + }); + if (!foundRole) { + throw new Error('Role not found'); + } + await db.insert(schema.user_roles_table).values({ + user_id: insertedUser?.id, + role_id: foundRole?.id, + primary: role?.primary, + }); + }), + ); + }), + ); +} diff --git a/src/lib/server/api/mfa/tables/recovery-codes.table.ts b/src/lib/server/api/mfa/tables/recovery-codes.table.ts new file mode 100644 index 0000000..f248535 --- /dev/null +++ b/src/lib/server/api/mfa/tables/recovery-codes.table.ts @@ -0,0 +1,46 @@ +import {getTableColumns, relations, type InferSelectModel} from 'drizzle-orm'; +import {boolean, pgTable, text, uuid} from 'drizzle-orm/pg-core'; +import {id, timestamps} from '../../common/utils/drizzle'; +import { users_table } from '../../users/tables/users.table'; +import { generateId } from '../../common/utils/crypto'; + +/* -------------------------------------------------------------------------- */ +/* Table */ +/* -------------------------------------------------------------------------- */ +export const recovery_codes_table = pgTable('recovery_codes', { + id: id() + .primaryKey() + .$defaultFn(() => generateId()), + user_id: id() + .notNull() + .references(() => users_table.id) + .unique('recovery_codes_user_id_unique'), + code: text().notNull(), + used: boolean().default(false), + ...timestamps, +}); + +/* -------------------------------------------------------------------------- */ +/* Relations */ +/* -------------------------------------------------------------------------- */ +export const recoveryCodesRelations = relations(recovery_codes_table, ({ one }) => ({ + user: one(users_table, { + fields: [recovery_codes_table.user_id], + references: [users_table.id], + }), +})); + +/* -------------------------------------------------------------------------- */ +/* Types */ +/* -------------------------------------------------------------------------- */ +export type RecoveryCodesTable = InferSelectModel; + +export const recoveryCodesColumns = getTableColumns(recovery_codes_table); + +export const publicRecoveryCodesColumns = { + id: recoveryCodesColumns.id, + user_id: recoveryCodesColumns.user_id, + code: recoveryCodesColumns.code, + used: recoveryCodesColumns.used, + ...timestamps, +}; diff --git a/src/lib/server/api/mfa/tables/two-factor.table.ts b/src/lib/server/api/mfa/tables/two-factor.table.ts index b432619..18ad99a 100644 --- a/src/lib/server/api/mfa/tables/two-factor.table.ts +++ b/src/lib/server/api/mfa/tables/two-factor.table.ts @@ -1,36 +1,44 @@ -import { type InferSelectModel, relations } from 'drizzle-orm'; +import { type InferSelectModel, relations, getTableColumns } from 'drizzle-orm'; import { boolean, pgTable, text, timestamp, uuid } from 'drizzle-orm/pg-core'; import { id, timestamps } from '../../common/utils/drizzle'; -import { usersTable } from '../../users/tables/users.table'; +import { users_table } from '../../users/tables/users.table'; import { generateId } from '../../common/utils/crypto'; /* -------------------------------------------------------------------------- */ /* Table */ /* -------------------------------------------------------------------------- */ -export const twoFactorTable = pgTable('two_factor', { +export const two_factor_table = pgTable('two_factor', { id: id() .primaryKey() .$defaultFn(() => generateId()), - secret: text().notNull(), - enabled: boolean().notNull().default(false), user_id: id() .notNull() - .references(() => usersTable.id) + .references(() => users_table.id) .unique('two_factor_user_id_unique'), + secret: text().notNull(), + enabled: boolean().notNull().default(false), ...timestamps, }); /* -------------------------------------------------------------------------- */ /* Relations */ /* -------------------------------------------------------------------------- */ -export const emailVerificationsRelations = relations(twoFactorTable, ({ one }) => ({ - user: one(usersTable, { - fields: [twoFactorTable.user_id], - references: [usersTable.id], +export const emailVerificationsRelations = relations(two_factor_table, ({ one }) => ({ + user: one(users_table, { + fields: [two_factor_table.user_id], + references: [users_table.id], }), })); /* -------------------------------------------------------------------------- */ /* Types */ /* -------------------------------------------------------------------------- */ -export type TwoFactor = InferSelectModel; +export type TwoFactor = InferSelectModel; + +export const twoFactorColumns = getTableColumns(two_factor_table); + +export const publicTwoFactorColumns = { + id: twoFactorColumns.id, + user_id: twoFactorColumns.user_id, + ...timestamps, +}; \ No newline at end of file diff --git a/src/lib/server/api/roles/tables/roles.table.ts b/src/lib/server/api/roles/tables/roles.table.ts index d9736e5..291d90a 100644 --- a/src/lib/server/api/roles/tables/roles.table.ts +++ b/src/lib/server/api/roles/tables/roles.table.ts @@ -1,6 +1,6 @@ -import { type InferSelectModel, relations } from 'drizzle-orm'; +import { type InferSelectModel, relations, getTableColumns } from 'drizzle-orm'; import { pgTable, text } from 'drizzle-orm/pg-core'; -import { user_roles } from '../../users/tables/user-roles.table'; +import { user_roles_table } from '../../users/tables/user-roles.table'; import { timestamps } from '../../common/utils/drizzle'; import { id } from '../../common/utils/drizzle'; import { generateId } from '../../common/utils/crypto'; @@ -15,7 +15,7 @@ export enum RoleName { /* -------------------------------------------------------------------------- */ /* Table */ /* -------------------------------------------------------------------------- */ -export const rolesTable = pgTable('roles', { +export const roles_table = pgTable('roles', { id: id() .primaryKey() .$defaultFn(() => generateId()), @@ -26,13 +26,22 @@ export const rolesTable = pgTable('roles', { /* -------------------------------------------------------------------------- */ /* Relations */ /* -------------------------------------------------------------------------- */ -export const role_relations = relations(rolesTable, ({ many }) => ({ - user_roles: many(user_roles), +export const role_relations = relations(roles_table, ({ many }) => ({ + user_roles: many(user_roles_table), })); /* -------------------------------------------------------------------------- */ /* Types */ /* -------------------------------------------------------------------------- */ -export type Roles = InferSelectModel; +export type Roles = InferSelectModel; +export type RolesWithRelations = Roles & {}; + +const roleColumns = getTableColumns(roles_table); + +export const publicRoleColumns = { + id: roleColumns.id, + name: roleColumns.name, + ...timestamps, +}; diff --git a/src/lib/server/api/users/tables/credentials.table.ts b/src/lib/server/api/users/tables/credentials.table.ts index 9dbc99f..02e1ad6 100644 --- a/src/lib/server/api/users/tables/credentials.table.ts +++ b/src/lib/server/api/users/tables/credentials.table.ts @@ -1,7 +1,7 @@ -import type { InferSelectModel } from 'drizzle-orm'; +import { relations, type InferSelectModel, getTableColumns } from 'drizzle-orm'; import { pgTable, text } from 'drizzle-orm/pg-core'; import { id, timestamps } from '../../common/utils/drizzle'; -import { usersTable } from './users.table'; +import { users_table } from './users.table'; import { generateId } from '../../common/utils/crypto'; /* -------------------------------------------------------------------------- */ @@ -14,16 +14,37 @@ export enum CredentialsType { HOTP = 'hotp', } -export const credentialsTable = pgTable('credentials', { +export const credentials_table = pgTable('credentials', { id: id() .primaryKey() .$defaultFn(() => generateId()), user_id: id() .notNull() - .references(() => usersTable.id, { onDelete: 'cascade' }), + .references(() => users_table.id, { onDelete: 'cascade' }), type: text().notNull().default(CredentialsType.PASSWORD), secret_data: text().notNull(), ...timestamps, }); -export type Credentials = InferSelectModel; +/* -------------------------------------------------------------------------- */ +/* Relations */ +/* -------------------------------------------------------------------------- */ +export const credentialsRelations = relations(credentials_table, ({ one }) => ({ + user: one(users_table, { + fields: [credentials_table.user_id], + references: [users_table.id], + }), +})); + +/* -------------------------------------------------------------------------- */ +/* Types */ +/* -------------------------------------------------------------------------- */ +export type Credentials = InferSelectModel; + +const credentialsColumns = getTableColumns(credentials_table); + +export const publicCredentialsColumns = { + id: credentialsColumns.id, + user_id: credentialsColumns.user_id, + ...timestamps, +}; \ No newline at end of file diff --git a/src/lib/server/api/users/tables/user-roles.table.ts b/src/lib/server/api/users/tables/user-roles.table.ts index adbb1b4..1fd95a2 100644 --- a/src/lib/server/api/users/tables/user-roles.table.ts +++ b/src/lib/server/api/users/tables/user-roles.table.ts @@ -1,23 +1,23 @@ import { type InferSelectModel, relations, getTableColumns } from 'drizzle-orm'; import { boolean, pgTable, text, uuid } from 'drizzle-orm/pg-core'; -import { rolesTable } from '../../roles/tables/roles.table'; -import { usersTable } from './users.table'; +import { roles_table } from '../../roles/tables/roles.table'; +import { users_table } from './users.table'; import { generateId } from '../../common/utils/crypto'; import { id, timestamps } from '../../common/utils/drizzle'; /* -------------------------------------------------------------------------- */ /* Table */ /* -------------------------------------------------------------------------- */ -export const user_roles = pgTable('user_roles', { +export const user_roles_table = pgTable('user_roles', { id: id() .primaryKey() .$defaultFn(() => generateId()), - user_id: uuid() + user_id: id() .notNull() - .references(() => usersTable.id, { onDelete: 'cascade' }), - role_id: uuid() + .references(() => users_table.id, { onDelete: 'cascade' }), + role_id: id() .notNull() - .references(() => rolesTable.id, { onDelete: 'cascade' }), + .references(() => roles_table.id, { onDelete: 'cascade' }), primary: boolean().default(false), ...timestamps, }); @@ -25,26 +25,26 @@ export const user_roles = pgTable('user_roles', { /* -------------------------------------------------------------------------- */ /* Relations */ /* -------------------------------------------------------------------------- */ -export const user_role_relations = relations(user_roles, ({ one }) => ({ - role: one(rolesTable, { - fields: [user_roles.role_id], - references: [rolesTable.id], +export const user_role_relations = relations(user_roles_table, ({ one }) => ({ + role: one(roles_table, { + fields: [user_roles_table.role_id], + references: [roles_table.id], }), - user: one(usersTable, { - fields: [user_roles.user_id], - references: [usersTable.id], + user: one(users_table, { + fields: [user_roles_table.user_id], + references: [users_table.id], }), })); /* -------------------------------------------------------------------------- */ /* Types */ /* -------------------------------------------------------------------------- */ -export type UserRoles = InferSelectModel; +export type UserRoles = InferSelectModel; export type UserRolesWithRelations = UserRoles & {}; -const userRolesColumns = getTableColumns(user_roles); +const userRolesColumns = getTableColumns(user_roles_table); -export const publicUserColumns = { +export const publicUserRolesColumns = { id: userRolesColumns.id, ...timestamps, }; diff --git a/src/lib/server/api/users/tables/users.table.ts b/src/lib/server/api/users/tables/users.table.ts index ca00577..bbbc14c 100644 --- a/src/lib/server/api/users/tables/users.table.ts +++ b/src/lib/server/api/users/tables/users.table.ts @@ -2,12 +2,12 @@ import { boolean, pgTable, text } from 'drizzle-orm/pg-core'; import { getTableColumns, type InferSelectModel, relations } from 'drizzle-orm'; import { citext, id, timestamps } from '../../common/utils/drizzle'; import { generateId } from '../../common/utils/crypto'; -import { user_roles } from './user-roles.table'; +import { user_roles_table } from './user-roles.table'; /* -------------------------------------------------------------------------- */ /* Table */ /* -------------------------------------------------------------------------- */ -export const usersTable = pgTable('users', { +export const users_table = pgTable('users', { id: id() .primaryKey() .$defaultFn(() => generateId()), @@ -24,17 +24,17 @@ export const usersTable = pgTable('users', { /* -------------------------------------------------------------------------- */ /* Relations */ /* -------------------------------------------------------------------------- */ -export const userRelations = relations(usersTable, ({ many }) => ({ - user_roles: many(user_roles), +export const userRelations = relations(users_table, ({ many }) => ({ + user_roles: many(user_roles_table), })); /* -------------------------------------------------------------------------- */ /* Types */ /* -------------------------------------------------------------------------- */ -export type User = InferSelectModel; +export type User = InferSelectModel; export type UserWithRelations = User & {}; -const userColumns = getTableColumns(usersTable); +const userColumns = getTableColumns(users_table); export const publicUserColumns = { id: userColumns.id, diff --git a/src/lib/server/api/users/users.repository.ts b/src/lib/server/api/users/users.repository.ts index 91fdeed..1ba4b4a 100644 --- a/src/lib/server/api/users/users.repository.ts +++ b/src/lib/server/api/users/users.repository.ts @@ -1,6 +1,6 @@ import { injectable } from '@needle-di/core'; import { takeFirst, takeFirstOrThrow } from '../common/utils/drizzle'; -import { usersTable } from './tables/users.table'; +import { users_table } from './tables/users.table'; import { eq, type InferSelectModel } from 'drizzle-orm'; import { NotFound } from '../common/utils/exceptions'; import { DrizzleRepository } from '../common/factories/drizzle-repository.factory'; @@ -8,7 +8,7 @@ import { DrizzleRepository } from '../common/factories/drizzle-repository.factor /* -------------------------------------------------------------------------- */ /* Types */ /* -------------------------------------------------------------------------- */ -type Create = Pick, 'avatar' | 'email'>; +type Create = Pick, 'avatar' | 'email'>; type Update = Partial; /* -------------------------------------------------------------------------- */ @@ -16,25 +16,25 @@ type Update = Partial; /* -------------------------------------------------------------------------- */ @injectable() export class UsersRepository extends DrizzleRepository { - async findOneById(id: string, db = this.drizzle.db) { - return db.select().from(usersTable).where(eq(usersTable.id, id)).then(takeFirst); - } + async findOneById(id: string, db = this.drizzle.db) { + return db.select().from(users_table).where(eq(users_table.id, id)).then(takeFirst); + } - async findOneByEmail(email: string, db = this.drizzle.db) { - return db.select().from(usersTable).where(eq(usersTable.email, email)).then(takeFirst); - } + async findOneByEmail(email: string, db = this.drizzle.db) { + return db.select().from(users_table).where(eq(users_table.email, email)).then(takeFirst); + } - async findOneByIdOrThrow(id: string, db = this.drizzle.db) { - const user = await this.findOneById(id, db); - if (!user) throw NotFound('User not found'); - return user; - } + async findOneByIdOrThrow(id: string, db = this.drizzle.db) { + const user = await this.findOneById(id, db); + if (!user) throw NotFound('User not found'); + return user; + } - async update(id: string, data: Update, db = this.drizzle.db) { - return db.update(usersTable).set(data).where(eq(usersTable.id, id)).returning(); - } + async update(id: string, data: Update, db = this.drizzle.db) { + return db.update(users_table).set(data).where(eq(users_table.id, id)).returning(); + } - async create(data: Create, db = this.drizzle.db) { - return db.insert(usersTable).values(data).returning().then(takeFirstOrThrow); - } + async create(data: Create, db = this.drizzle.db) { + return db.insert(users_table).values(data).returning().then(takeFirstOrThrow); + } }