diff --git a/.env.example b/.env.example index 128bd9f..f492443 100644 --- a/.env.example +++ b/.env.example @@ -1,8 +1,12 @@ # Required environment variables +DATABASE_PASSWORD= SESSION_SECRET= # Optional environment variables -DATABASE_URL= +DATABASE_HOST= +DATABASE_PORT= +DATABASE_USER= +DATABASE_NAME= SESSION_EXPIRES= USER_CLIENT_CHALLENGE_EXPIRES= SESSION_UPGRADE_CHALLENGE_EXPIRES= diff --git a/Dockerfile b/Dockerfile index eec42f6..f809fbc 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,6 +2,10 @@ FROM node:22-alpine AS base WORKDIR /app +RUN apk add --no-cache bash curl && \ + curl -o /usr/local/bin/wait-for-it https://raw.githubusercontent.com/vishnubob/wait-for-it/master/wait-for-it.sh && \ + chmod +x /usr/local/bin/wait-for-it + RUN npm install -g pnpm@9 COPY pnpm-lock.yaml . @@ -10,10 +14,9 @@ FROM base AS build RUN pnpm fetch COPY . . -RUN pnpm install --offline -RUN pnpm build - -RUN sed -i "s/http\.createServer()/http.createServer({ requestTimeout: 0 })/g" ./build/index.js +RUN pnpm install --offline && \ + pnpm build && \ + sed -i "s/http\.createServer()/http.createServer({ requestTimeout: 0 })/g" ./build/index.js # Deploy Stage FROM base @@ -23,9 +26,7 @@ COPY package.json . RUN pnpm install --offline --prod COPY --from=build /app/build ./build -COPY drizzle ./drizzle EXPOSE 3000 ENV BODY_SIZE_LIMIT=Infinity - -CMD ["node", "./build/index.js"] +CMD ["bash", "-c", "wait-for-it ${DATABASE_HOST:-localhost}:${DATABASE_PORT:-5432} -- node ./build/index.js"] diff --git a/README.md b/README.md index 45f5c5a..ec1918c 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ vim .env # 아래를 참고하여 환경 변수를 설정해 주세요. docker compose up --build -d ``` -모든 데이터는 `./data` 디렉터리에 저장될 거예요. +모든 데이터는 `./data` 디렉터리에 아래에 저장될 거예요. ### Environment Variables @@ -31,7 +31,8 @@ docker compose up --build -d |이름|필수|기본값|설명| |:-|:-:|:-:|:-| -|`SESSION_SECRET`|Y||Session ID의 서명을 위해 사용돼요. 안전한 값으로 설정해 주세요.| +|`DATABASE_PASSWORD`|Y||데이터베이스에 접근하기 위해 필요한 비밀번호예요. 안전한 값으로 설정해 주세요.| +|`SESSION_SECRET`|Y||Session ID의 서명에 사용되는 비밀번호예요. 안전한 값으로 설정해 주세요.| |`SESSION_EXPIRES`||`14d`|Session의 유효 시간이에요. Session은 마지막으로 사용된 후 설정된 유효 시간이 지나면 자동으로 삭제돼요.| |`USER_CLIENT_CHALLENGE_EXPIRES`||`5m`|암호 키를 서버에 처음 등록할 때 사용되는 챌린지의 유효 시간이에요.| |`SESSION_UPGRADE_CHALLENGE_EXPIRES`||`5m`|암호 키와 함께 로그인할 때 사용되는 챌린지의 유효 시간이에요.| diff --git a/docker-compose.dev.yaml b/docker-compose.dev.yaml new file mode 100644 index 0000000..f6570a5 --- /dev/null +++ b/docker-compose.dev.yaml @@ -0,0 +1,15 @@ +services: + database: + image: postgres:17.2 + restart: on-failure + volumes: + - database:/var/lib/postgresql/data + environment: + - POSTGRES_USER=${DATABASE_USER:-} + - POSTGRES_PASSWORD=${DATABASE_PASSWORD:?} # Required + - POSTGRES_DB=${DATABASE_NAME:-} + ports: + - ${DATABASE_PORT:-5432}:5432 + +volumes: + database: diff --git a/docker-compose.yaml b/docker-compose.yaml index aecd8c8..dc7f392 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -1,13 +1,17 @@ services: server: build: . - restart: unless-stopped + restart: on-failure + depends_on: + - database user: ${CONTAINER_UID:-0}:${CONTAINER_GID:-0} volumes: - - ./data:/app/data + - ./data/library:/app/data/library environment: # ArkVault - - DATABASE_URL=/app/data/database.sqlite + - DATABASE_HOST=database + - DATABASE_USER=arkvault + - DATABASE_PASSWORD=${DATABASE_PASSWORD:?} # Required - SESSION_SECRET=${SESSION_SECRET:?} # Required - SESSION_EXPIRES - USER_CLIENT_CHALLENGE_EXPIRES @@ -19,3 +23,13 @@ services: - NODE_ENV=${NODE_ENV:-production} ports: - ${PORT:-80}:3000 + + database: + image: postgres:17.2-alpine + restart: on-failure + user: ${CONTAINER_UID:-0}:${CONTAINER_GID:-0} + volumes: + - ./data/database:/var/lib/postgresql/data + environment: + - POSTGRES_USER=arkvault + - POSTGRES_PASSWORD=${DATABASE_PASSWORD:?} diff --git a/drizzle.config.ts b/drizzle.config.ts deleted file mode 100644 index c0b54d5..0000000 --- a/drizzle.config.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { defineConfig } from "drizzle-kit"; - -export default defineConfig({ - schema: "./src/lib/server/db/schema", - - dbCredentials: { - url: process.env.DATABASE_URL || "local.db", - }, - - verbose: true, - strict: true, - dialect: "sqlite", -}); diff --git a/drizzle/0000_regular_the_watchers.sql b/drizzle/0000_regular_the_watchers.sql deleted file mode 100644 index 6cbdec8..0000000 --- a/drizzle/0000_regular_the_watchers.sql +++ /dev/null @@ -1,178 +0,0 @@ -CREATE TABLE `client` ( - `id` integer PRIMARY KEY AUTOINCREMENT NOT NULL, - `encryption_public_key` text NOT NULL, - `signature_public_key` text NOT NULL -); ---> statement-breakpoint -CREATE TABLE `user_client` ( - `user_id` integer NOT NULL, - `client_id` integer NOT NULL, - `state` text DEFAULT 'challenging' NOT NULL, - PRIMARY KEY(`client_id`, `user_id`), - FOREIGN KEY (`user_id`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE no action, - FOREIGN KEY (`client_id`) REFERENCES `client`(`id`) ON UPDATE no action ON DELETE no action -); ---> statement-breakpoint -CREATE TABLE `user_client_challenge` ( - `id` integer PRIMARY KEY NOT NULL, - `user_id` integer NOT NULL, - `client_id` integer NOT NULL, - `answer` text NOT NULL, - `allowed_ip` text NOT NULL, - `expires_at` integer NOT NULL, - FOREIGN KEY (`user_id`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE no action, - FOREIGN KEY (`client_id`) REFERENCES `client`(`id`) ON UPDATE no action ON DELETE no action, - FOREIGN KEY (`user_id`,`client_id`) REFERENCES `user_client`(`user_id`,`client_id`) ON UPDATE no action ON DELETE no action -); ---> statement-breakpoint -CREATE TABLE `directory` ( - `id` integer PRIMARY KEY AUTOINCREMENT NOT NULL, - `parent_id` integer, - `user_id` integer NOT NULL, - `master_encryption_key_version` integer NOT NULL, - `encrypted_data_encryption_key` text NOT NULL, - `data_encryption_key_version` integer NOT NULL, - `encrypted_name` text NOT NULL, - FOREIGN KEY (`user_id`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE no action, - FOREIGN KEY (`parent_id`) REFERENCES `directory`(`id`) ON UPDATE no action ON DELETE no action, - FOREIGN KEY (`user_id`,`master_encryption_key_version`) REFERENCES `master_encryption_key`(`user_id`,`version`) ON UPDATE no action ON DELETE no action -); ---> statement-breakpoint -CREATE TABLE `directory_log` ( - `id` integer PRIMARY KEY AUTOINCREMENT NOT NULL, - `directory_id` integer NOT NULL, - `timestamp` integer NOT NULL, - `action` text NOT NULL, - `new_name` text, - FOREIGN KEY (`directory_id`) REFERENCES `directory`(`id`) ON UPDATE no action ON DELETE cascade -); ---> statement-breakpoint -CREATE TABLE `file` ( - `id` integer PRIMARY KEY AUTOINCREMENT NOT NULL, - `parent_id` integer, - `user_id` integer NOT NULL, - `path` text NOT NULL, - `master_encryption_key_version` integer NOT NULL, - `encrypted_data_encryption_key` text NOT NULL, - `data_encryption_key_version` integer NOT NULL, - `hmac_secret_key_version` integer, - `content_hmac` text, - `content_type` text NOT NULL, - `encrypted_content_iv` text NOT NULL, - `encrypted_content_hash` text NOT NULL, - `encrypted_name` text NOT NULL, - `encrypted_created_at` text, - `encrypted_last_modified_at` text NOT NULL, - FOREIGN KEY (`parent_id`) REFERENCES `directory`(`id`) ON UPDATE no action ON DELETE no action, - FOREIGN KEY (`user_id`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE no action, - FOREIGN KEY (`user_id`,`master_encryption_key_version`) REFERENCES `master_encryption_key`(`user_id`,`version`) ON UPDATE no action ON DELETE no action, - FOREIGN KEY (`user_id`,`hmac_secret_key_version`) REFERENCES `hmac_secret_key`(`user_id`,`version`) ON UPDATE no action ON DELETE no action -); ---> statement-breakpoint -CREATE TABLE `file_log` ( - `id` integer PRIMARY KEY AUTOINCREMENT NOT NULL, - `file_id` integer NOT NULL, - `timestamp` integer NOT NULL, - `action` text NOT NULL, - `new_name` text, - FOREIGN KEY (`file_id`) REFERENCES `file`(`id`) ON UPDATE no action ON DELETE cascade -); ---> statement-breakpoint -CREATE TABLE `hmac_secret_key` ( - `user_id` integer NOT NULL, - `version` integer NOT NULL, - `state` text NOT NULL, - `master_encryption_key_version` integer NOT NULL, - `encrypted_key` text NOT NULL, - PRIMARY KEY(`user_id`, `version`), - FOREIGN KEY (`user_id`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE no action, - FOREIGN KEY (`user_id`,`master_encryption_key_version`) REFERENCES `master_encryption_key`(`user_id`,`version`) ON UPDATE no action ON DELETE no action -); ---> statement-breakpoint -CREATE TABLE `hmac_secret_key_log` ( - `id` integer PRIMARY KEY AUTOINCREMENT NOT NULL, - `user_id` integer NOT NULL, - `hmac_secret_key_version` integer NOT NULL, - `timestamp` integer NOT NULL, - `action` text NOT NULL, - `action_by` integer, - FOREIGN KEY (`user_id`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE no action, - FOREIGN KEY (`action_by`) REFERENCES `client`(`id`) ON UPDATE no action ON DELETE no action, - FOREIGN KEY (`user_id`,`hmac_secret_key_version`) REFERENCES `hmac_secret_key`(`user_id`,`version`) ON UPDATE no action ON DELETE no action -); ---> statement-breakpoint -CREATE TABLE `client_master_encryption_key` ( - `user_id` integer NOT NULL, - `client_id` integer NOT NULL, - `version` integer NOT NULL, - `encrypted_key` text NOT NULL, - `encrypted_key_signature` text NOT NULL, - PRIMARY KEY(`client_id`, `user_id`, `version`), - FOREIGN KEY (`user_id`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE no action, - FOREIGN KEY (`client_id`) REFERENCES `client`(`id`) ON UPDATE no action ON DELETE no action, - FOREIGN KEY (`user_id`,`version`) REFERENCES `master_encryption_key`(`user_id`,`version`) ON UPDATE no action ON DELETE no action -); ---> statement-breakpoint -CREATE TABLE `master_encryption_key` ( - `user_id` integer NOT NULL, - `version` integer NOT NULL, - `state` text NOT NULL, - `retired_at` integer, - PRIMARY KEY(`user_id`, `version`), - FOREIGN KEY (`user_id`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE no action -); ---> statement-breakpoint -CREATE TABLE `master_encryption_key_log` ( - `id` integer PRIMARY KEY AUTOINCREMENT NOT NULL, - `user_id` integer NOT NULL, - `master_encryption_key_version` integer NOT NULL, - `timestamp` integer NOT NULL, - `action` text NOT NULL, - `action_by` integer, - FOREIGN KEY (`user_id`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE no action, - FOREIGN KEY (`action_by`) REFERENCES `client`(`id`) ON UPDATE no action ON DELETE no action, - FOREIGN KEY (`user_id`,`master_encryption_key_version`) REFERENCES `master_encryption_key`(`user_id`,`version`) ON UPDATE no action ON DELETE no action -); ---> statement-breakpoint -CREATE TABLE `session` ( - `id` text PRIMARY KEY NOT NULL, - `user_id` integer NOT NULL, - `client_id` integer, - `created_at` integer NOT NULL, - `last_used_at` integer NOT NULL, - `last_used_by_ip` text, - `last_used_by_user_agent` text, - FOREIGN KEY (`user_id`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE no action, - FOREIGN KEY (`client_id`) REFERENCES `client`(`id`) ON UPDATE no action ON DELETE no action -); ---> statement-breakpoint -CREATE TABLE `session_upgrade_challenge` ( - `id` integer PRIMARY KEY NOT NULL, - `session_id` text NOT NULL, - `client_id` integer NOT NULL, - `answer` text NOT NULL, - `allowed_ip` text NOT NULL, - `expires_at` integer NOT NULL, - FOREIGN KEY (`session_id`) REFERENCES `session`(`id`) ON UPDATE no action ON DELETE no action, - FOREIGN KEY (`client_id`) REFERENCES `client`(`id`) ON UPDATE no action ON DELETE no action -); ---> statement-breakpoint -CREATE TABLE `user` ( - `id` integer PRIMARY KEY AUTOINCREMENT NOT NULL, - `email` text NOT NULL, - `password` text NOT NULL, - `nickname` text NOT NULL -); ---> statement-breakpoint -CREATE UNIQUE INDEX `client_encryption_public_key_unique` ON `client` (`encryption_public_key`);--> statement-breakpoint -CREATE UNIQUE INDEX `client_signature_public_key_unique` ON `client` (`signature_public_key`);--> statement-breakpoint -CREATE UNIQUE INDEX `client_encryption_public_key_signature_public_key_unique` ON `client` (`encryption_public_key`,`signature_public_key`);--> statement-breakpoint -CREATE UNIQUE INDEX `user_client_challenge_answer_unique` ON `user_client_challenge` (`answer`);--> statement-breakpoint -CREATE UNIQUE INDEX `directory_encrypted_data_encryption_key_unique` ON `directory` (`encrypted_data_encryption_key`);--> statement-breakpoint -CREATE UNIQUE INDEX `file_path_unique` ON `file` (`path`);--> statement-breakpoint -CREATE UNIQUE INDEX `file_encrypted_data_encryption_key_unique` ON `file` (`encrypted_data_encryption_key`);--> statement-breakpoint -CREATE UNIQUE INDEX `hmac_secret_key_encrypted_key_unique` ON `hmac_secret_key` (`encrypted_key`);--> statement-breakpoint -CREATE UNIQUE INDEX `session_user_id_client_id_unique` ON `session` (`user_id`,`client_id`);--> statement-breakpoint -CREATE UNIQUE INDEX `session_upgrade_challenge_session_id_unique` ON `session_upgrade_challenge` (`session_id`);--> statement-breakpoint -CREATE UNIQUE INDEX `session_upgrade_challenge_answer_unique` ON `session_upgrade_challenge` (`answer`);--> statement-breakpoint -CREATE UNIQUE INDEX `user_email_unique` ON `user` (`email`); \ No newline at end of file diff --git a/drizzle/meta/0000_snapshot.json b/drizzle/meta/0000_snapshot.json deleted file mode 100644 index 27a42bc..0000000 --- a/drizzle/meta/0000_snapshot.json +++ /dev/null @@ -1,1308 +0,0 @@ -{ - "version": "6", - "dialect": "sqlite", - "id": "396a26d6-6f55-4162-a23e-c1117f3a3757", - "prevId": "00000000-0000-0000-0000-000000000000", - "tables": { - "client": { - "name": "client", - "columns": { - "id": { - "name": "id", - "type": "integer", - "primaryKey": true, - "notNull": true, - "autoincrement": true - }, - "encryption_public_key": { - "name": "encryption_public_key", - "type": "text", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "signature_public_key": { - "name": "signature_public_key", - "type": "text", - "primaryKey": false, - "notNull": true, - "autoincrement": false - } - }, - "indexes": { - "client_encryption_public_key_unique": { - "name": "client_encryption_public_key_unique", - "columns": [ - "encryption_public_key" - ], - "isUnique": true - }, - "client_signature_public_key_unique": { - "name": "client_signature_public_key_unique", - "columns": [ - "signature_public_key" - ], - "isUnique": true - }, - "client_encryption_public_key_signature_public_key_unique": { - "name": "client_encryption_public_key_signature_public_key_unique", - "columns": [ - "encryption_public_key", - "signature_public_key" - ], - "isUnique": true - } - }, - "foreignKeys": {}, - "compositePrimaryKeys": {}, - "uniqueConstraints": {} - }, - "user_client": { - "name": "user_client", - "columns": { - "user_id": { - "name": "user_id", - "type": "integer", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "client_id": { - "name": "client_id", - "type": "integer", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "state": { - "name": "state", - "type": "text", - "primaryKey": false, - "notNull": true, - "autoincrement": false, - "default": "'challenging'" - } - }, - "indexes": {}, - "foreignKeys": { - "user_client_user_id_user_id_fk": { - "name": "user_client_user_id_user_id_fk", - "tableFrom": "user_client", - "tableTo": "user", - "columnsFrom": [ - "user_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "no action", - "onUpdate": "no action" - }, - "user_client_client_id_client_id_fk": { - "name": "user_client_client_id_client_id_fk", - "tableFrom": "user_client", - "tableTo": "client", - "columnsFrom": [ - "client_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "no action", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": { - "user_client_user_id_client_id_pk": { - "columns": [ - "client_id", - "user_id" - ], - "name": "user_client_user_id_client_id_pk" - } - }, - "uniqueConstraints": {} - }, - "user_client_challenge": { - "name": "user_client_challenge", - "columns": { - "id": { - "name": "id", - "type": "integer", - "primaryKey": true, - "notNull": true, - "autoincrement": false - }, - "user_id": { - "name": "user_id", - "type": "integer", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "client_id": { - "name": "client_id", - "type": "integer", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "answer": { - "name": "answer", - "type": "text", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "allowed_ip": { - "name": "allowed_ip", - "type": "text", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "expires_at": { - "name": "expires_at", - "type": "integer", - "primaryKey": false, - "notNull": true, - "autoincrement": false - } - }, - "indexes": { - "user_client_challenge_answer_unique": { - "name": "user_client_challenge_answer_unique", - "columns": [ - "answer" - ], - "isUnique": true - } - }, - "foreignKeys": { - "user_client_challenge_user_id_user_id_fk": { - "name": "user_client_challenge_user_id_user_id_fk", - "tableFrom": "user_client_challenge", - "tableTo": "user", - "columnsFrom": [ - "user_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "no action", - "onUpdate": "no action" - }, - "user_client_challenge_client_id_client_id_fk": { - "name": "user_client_challenge_client_id_client_id_fk", - "tableFrom": "user_client_challenge", - "tableTo": "client", - "columnsFrom": [ - "client_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "no action", - "onUpdate": "no action" - }, - "user_client_challenge_user_id_client_id_user_client_user_id_client_id_fk": { - "name": "user_client_challenge_user_id_client_id_user_client_user_id_client_id_fk", - "tableFrom": "user_client_challenge", - "tableTo": "user_client", - "columnsFrom": [ - "user_id", - "client_id" - ], - "columnsTo": [ - "user_id", - "client_id" - ], - "onDelete": "no action", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {} - }, - "directory": { - "name": "directory", - "columns": { - "id": { - "name": "id", - "type": "integer", - "primaryKey": true, - "notNull": true, - "autoincrement": true - }, - "parent_id": { - "name": "parent_id", - "type": "integer", - "primaryKey": false, - "notNull": false, - "autoincrement": false - }, - "user_id": { - "name": "user_id", - "type": "integer", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "master_encryption_key_version": { - "name": "master_encryption_key_version", - "type": "integer", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "encrypted_data_encryption_key": { - "name": "encrypted_data_encryption_key", - "type": "text", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "data_encryption_key_version": { - "name": "data_encryption_key_version", - "type": "integer", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "encrypted_name": { - "name": "encrypted_name", - "type": "text", - "primaryKey": false, - "notNull": true, - "autoincrement": false - } - }, - "indexes": { - "directory_encrypted_data_encryption_key_unique": { - "name": "directory_encrypted_data_encryption_key_unique", - "columns": [ - "encrypted_data_encryption_key" - ], - "isUnique": true - } - }, - "foreignKeys": { - "directory_user_id_user_id_fk": { - "name": "directory_user_id_user_id_fk", - "tableFrom": "directory", - "tableTo": "user", - "columnsFrom": [ - "user_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "no action", - "onUpdate": "no action" - }, - "directory_parent_id_directory_id_fk": { - "name": "directory_parent_id_directory_id_fk", - "tableFrom": "directory", - "tableTo": "directory", - "columnsFrom": [ - "parent_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "no action", - "onUpdate": "no action" - }, - "directory_user_id_master_encryption_key_version_master_encryption_key_user_id_version_fk": { - "name": "directory_user_id_master_encryption_key_version_master_encryption_key_user_id_version_fk", - "tableFrom": "directory", - "tableTo": "master_encryption_key", - "columnsFrom": [ - "user_id", - "master_encryption_key_version" - ], - "columnsTo": [ - "user_id", - "version" - ], - "onDelete": "no action", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {} - }, - "directory_log": { - "name": "directory_log", - "columns": { - "id": { - "name": "id", - "type": "integer", - "primaryKey": true, - "notNull": true, - "autoincrement": true - }, - "directory_id": { - "name": "directory_id", - "type": "integer", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "timestamp": { - "name": "timestamp", - "type": "integer", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "action": { - "name": "action", - "type": "text", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "new_name": { - "name": "new_name", - "type": "text", - "primaryKey": false, - "notNull": false, - "autoincrement": false - } - }, - "indexes": {}, - "foreignKeys": { - "directory_log_directory_id_directory_id_fk": { - "name": "directory_log_directory_id_directory_id_fk", - "tableFrom": "directory_log", - "tableTo": "directory", - "columnsFrom": [ - "directory_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "cascade", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {} - }, - "file": { - "name": "file", - "columns": { - "id": { - "name": "id", - "type": "integer", - "primaryKey": true, - "notNull": true, - "autoincrement": true - }, - "parent_id": { - "name": "parent_id", - "type": "integer", - "primaryKey": false, - "notNull": false, - "autoincrement": false - }, - "user_id": { - "name": "user_id", - "type": "integer", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "path": { - "name": "path", - "type": "text", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "master_encryption_key_version": { - "name": "master_encryption_key_version", - "type": "integer", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "encrypted_data_encryption_key": { - "name": "encrypted_data_encryption_key", - "type": "text", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "data_encryption_key_version": { - "name": "data_encryption_key_version", - "type": "integer", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "hmac_secret_key_version": { - "name": "hmac_secret_key_version", - "type": "integer", - "primaryKey": false, - "notNull": false, - "autoincrement": false - }, - "content_hmac": { - "name": "content_hmac", - "type": "text", - "primaryKey": false, - "notNull": false, - "autoincrement": false - }, - "content_type": { - "name": "content_type", - "type": "text", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "encrypted_content_iv": { - "name": "encrypted_content_iv", - "type": "text", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "encrypted_content_hash": { - "name": "encrypted_content_hash", - "type": "text", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "encrypted_name": { - "name": "encrypted_name", - "type": "text", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "encrypted_created_at": { - "name": "encrypted_created_at", - "type": "text", - "primaryKey": false, - "notNull": false, - "autoincrement": false - }, - "encrypted_last_modified_at": { - "name": "encrypted_last_modified_at", - "type": "text", - "primaryKey": false, - "notNull": true, - "autoincrement": false - } - }, - "indexes": { - "file_path_unique": { - "name": "file_path_unique", - "columns": [ - "path" - ], - "isUnique": true - }, - "file_encrypted_data_encryption_key_unique": { - "name": "file_encrypted_data_encryption_key_unique", - "columns": [ - "encrypted_data_encryption_key" - ], - "isUnique": true - } - }, - "foreignKeys": { - "file_parent_id_directory_id_fk": { - "name": "file_parent_id_directory_id_fk", - "tableFrom": "file", - "tableTo": "directory", - "columnsFrom": [ - "parent_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "no action", - "onUpdate": "no action" - }, - "file_user_id_user_id_fk": { - "name": "file_user_id_user_id_fk", - "tableFrom": "file", - "tableTo": "user", - "columnsFrom": [ - "user_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "no action", - "onUpdate": "no action" - }, - "file_user_id_master_encryption_key_version_master_encryption_key_user_id_version_fk": { - "name": "file_user_id_master_encryption_key_version_master_encryption_key_user_id_version_fk", - "tableFrom": "file", - "tableTo": "master_encryption_key", - "columnsFrom": [ - "user_id", - "master_encryption_key_version" - ], - "columnsTo": [ - "user_id", - "version" - ], - "onDelete": "no action", - "onUpdate": "no action" - }, - "file_user_id_hmac_secret_key_version_hmac_secret_key_user_id_version_fk": { - "name": "file_user_id_hmac_secret_key_version_hmac_secret_key_user_id_version_fk", - "tableFrom": "file", - "tableTo": "hmac_secret_key", - "columnsFrom": [ - "user_id", - "hmac_secret_key_version" - ], - "columnsTo": [ - "user_id", - "version" - ], - "onDelete": "no action", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {} - }, - "file_log": { - "name": "file_log", - "columns": { - "id": { - "name": "id", - "type": "integer", - "primaryKey": true, - "notNull": true, - "autoincrement": true - }, - "file_id": { - "name": "file_id", - "type": "integer", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "timestamp": { - "name": "timestamp", - "type": "integer", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "action": { - "name": "action", - "type": "text", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "new_name": { - "name": "new_name", - "type": "text", - "primaryKey": false, - "notNull": false, - "autoincrement": false - } - }, - "indexes": {}, - "foreignKeys": { - "file_log_file_id_file_id_fk": { - "name": "file_log_file_id_file_id_fk", - "tableFrom": "file_log", - "tableTo": "file", - "columnsFrom": [ - "file_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "cascade", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {} - }, - "hmac_secret_key": { - "name": "hmac_secret_key", - "columns": { - "user_id": { - "name": "user_id", - "type": "integer", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "version": { - "name": "version", - "type": "integer", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "state": { - "name": "state", - "type": "text", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "master_encryption_key_version": { - "name": "master_encryption_key_version", - "type": "integer", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "encrypted_key": { - "name": "encrypted_key", - "type": "text", - "primaryKey": false, - "notNull": true, - "autoincrement": false - } - }, - "indexes": { - "hmac_secret_key_encrypted_key_unique": { - "name": "hmac_secret_key_encrypted_key_unique", - "columns": [ - "encrypted_key" - ], - "isUnique": true - } - }, - "foreignKeys": { - "hmac_secret_key_user_id_user_id_fk": { - "name": "hmac_secret_key_user_id_user_id_fk", - "tableFrom": "hmac_secret_key", - "tableTo": "user", - "columnsFrom": [ - "user_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "no action", - "onUpdate": "no action" - }, - "hmac_secret_key_user_id_master_encryption_key_version_master_encryption_key_user_id_version_fk": { - "name": "hmac_secret_key_user_id_master_encryption_key_version_master_encryption_key_user_id_version_fk", - "tableFrom": "hmac_secret_key", - "tableTo": "master_encryption_key", - "columnsFrom": [ - "user_id", - "master_encryption_key_version" - ], - "columnsTo": [ - "user_id", - "version" - ], - "onDelete": "no action", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": { - "hmac_secret_key_user_id_version_pk": { - "columns": [ - "user_id", - "version" - ], - "name": "hmac_secret_key_user_id_version_pk" - } - }, - "uniqueConstraints": {} - }, - "hmac_secret_key_log": { - "name": "hmac_secret_key_log", - "columns": { - "id": { - "name": "id", - "type": "integer", - "primaryKey": true, - "notNull": true, - "autoincrement": true - }, - "user_id": { - "name": "user_id", - "type": "integer", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "hmac_secret_key_version": { - "name": "hmac_secret_key_version", - "type": "integer", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "timestamp": { - "name": "timestamp", - "type": "integer", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "action": { - "name": "action", - "type": "text", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "action_by": { - "name": "action_by", - "type": "integer", - "primaryKey": false, - "notNull": false, - "autoincrement": false - } - }, - "indexes": {}, - "foreignKeys": { - "hmac_secret_key_log_user_id_user_id_fk": { - "name": "hmac_secret_key_log_user_id_user_id_fk", - "tableFrom": "hmac_secret_key_log", - "tableTo": "user", - "columnsFrom": [ - "user_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "no action", - "onUpdate": "no action" - }, - "hmac_secret_key_log_action_by_client_id_fk": { - "name": "hmac_secret_key_log_action_by_client_id_fk", - "tableFrom": "hmac_secret_key_log", - "tableTo": "client", - "columnsFrom": [ - "action_by" - ], - "columnsTo": [ - "id" - ], - "onDelete": "no action", - "onUpdate": "no action" - }, - "hmac_secret_key_log_user_id_hmac_secret_key_version_hmac_secret_key_user_id_version_fk": { - "name": "hmac_secret_key_log_user_id_hmac_secret_key_version_hmac_secret_key_user_id_version_fk", - "tableFrom": "hmac_secret_key_log", - "tableTo": "hmac_secret_key", - "columnsFrom": [ - "user_id", - "hmac_secret_key_version" - ], - "columnsTo": [ - "user_id", - "version" - ], - "onDelete": "no action", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {} - }, - "client_master_encryption_key": { - "name": "client_master_encryption_key", - "columns": { - "user_id": { - "name": "user_id", - "type": "integer", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "client_id": { - "name": "client_id", - "type": "integer", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "version": { - "name": "version", - "type": "integer", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "encrypted_key": { - "name": "encrypted_key", - "type": "text", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "encrypted_key_signature": { - "name": "encrypted_key_signature", - "type": "text", - "primaryKey": false, - "notNull": true, - "autoincrement": false - } - }, - "indexes": {}, - "foreignKeys": { - "client_master_encryption_key_user_id_user_id_fk": { - "name": "client_master_encryption_key_user_id_user_id_fk", - "tableFrom": "client_master_encryption_key", - "tableTo": "user", - "columnsFrom": [ - "user_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "no action", - "onUpdate": "no action" - }, - "client_master_encryption_key_client_id_client_id_fk": { - "name": "client_master_encryption_key_client_id_client_id_fk", - "tableFrom": "client_master_encryption_key", - "tableTo": "client", - "columnsFrom": [ - "client_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "no action", - "onUpdate": "no action" - }, - "client_master_encryption_key_user_id_version_master_encryption_key_user_id_version_fk": { - "name": "client_master_encryption_key_user_id_version_master_encryption_key_user_id_version_fk", - "tableFrom": "client_master_encryption_key", - "tableTo": "master_encryption_key", - "columnsFrom": [ - "user_id", - "version" - ], - "columnsTo": [ - "user_id", - "version" - ], - "onDelete": "no action", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": { - "client_master_encryption_key_user_id_client_id_version_pk": { - "columns": [ - "client_id", - "user_id", - "version" - ], - "name": "client_master_encryption_key_user_id_client_id_version_pk" - } - }, - "uniqueConstraints": {} - }, - "master_encryption_key": { - "name": "master_encryption_key", - "columns": { - "user_id": { - "name": "user_id", - "type": "integer", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "version": { - "name": "version", - "type": "integer", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "state": { - "name": "state", - "type": "text", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "retired_at": { - "name": "retired_at", - "type": "integer", - "primaryKey": false, - "notNull": false, - "autoincrement": false - } - }, - "indexes": {}, - "foreignKeys": { - "master_encryption_key_user_id_user_id_fk": { - "name": "master_encryption_key_user_id_user_id_fk", - "tableFrom": "master_encryption_key", - "tableTo": "user", - "columnsFrom": [ - "user_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "no action", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": { - "master_encryption_key_user_id_version_pk": { - "columns": [ - "user_id", - "version" - ], - "name": "master_encryption_key_user_id_version_pk" - } - }, - "uniqueConstraints": {} - }, - "master_encryption_key_log": { - "name": "master_encryption_key_log", - "columns": { - "id": { - "name": "id", - "type": "integer", - "primaryKey": true, - "notNull": true, - "autoincrement": true - }, - "user_id": { - "name": "user_id", - "type": "integer", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "master_encryption_key_version": { - "name": "master_encryption_key_version", - "type": "integer", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "timestamp": { - "name": "timestamp", - "type": "integer", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "action": { - "name": "action", - "type": "text", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "action_by": { - "name": "action_by", - "type": "integer", - "primaryKey": false, - "notNull": false, - "autoincrement": false - } - }, - "indexes": {}, - "foreignKeys": { - "master_encryption_key_log_user_id_user_id_fk": { - "name": "master_encryption_key_log_user_id_user_id_fk", - "tableFrom": "master_encryption_key_log", - "tableTo": "user", - "columnsFrom": [ - "user_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "no action", - "onUpdate": "no action" - }, - "master_encryption_key_log_action_by_client_id_fk": { - "name": "master_encryption_key_log_action_by_client_id_fk", - "tableFrom": "master_encryption_key_log", - "tableTo": "client", - "columnsFrom": [ - "action_by" - ], - "columnsTo": [ - "id" - ], - "onDelete": "no action", - "onUpdate": "no action" - }, - "master_encryption_key_log_user_id_master_encryption_key_version_master_encryption_key_user_id_version_fk": { - "name": "master_encryption_key_log_user_id_master_encryption_key_version_master_encryption_key_user_id_version_fk", - "tableFrom": "master_encryption_key_log", - "tableTo": "master_encryption_key", - "columnsFrom": [ - "user_id", - "master_encryption_key_version" - ], - "columnsTo": [ - "user_id", - "version" - ], - "onDelete": "no action", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {} - }, - "session": { - "name": "session", - "columns": { - "id": { - "name": "id", - "type": "text", - "primaryKey": true, - "notNull": true, - "autoincrement": false - }, - "user_id": { - "name": "user_id", - "type": "integer", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "client_id": { - "name": "client_id", - "type": "integer", - "primaryKey": false, - "notNull": false, - "autoincrement": false - }, - "created_at": { - "name": "created_at", - "type": "integer", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "last_used_at": { - "name": "last_used_at", - "type": "integer", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "last_used_by_ip": { - "name": "last_used_by_ip", - "type": "text", - "primaryKey": false, - "notNull": false, - "autoincrement": false - }, - "last_used_by_user_agent": { - "name": "last_used_by_user_agent", - "type": "text", - "primaryKey": false, - "notNull": false, - "autoincrement": false - } - }, - "indexes": { - "session_user_id_client_id_unique": { - "name": "session_user_id_client_id_unique", - "columns": [ - "user_id", - "client_id" - ], - "isUnique": true - } - }, - "foreignKeys": { - "session_user_id_user_id_fk": { - "name": "session_user_id_user_id_fk", - "tableFrom": "session", - "tableTo": "user", - "columnsFrom": [ - "user_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "no action", - "onUpdate": "no action" - }, - "session_client_id_client_id_fk": { - "name": "session_client_id_client_id_fk", - "tableFrom": "session", - "tableTo": "client", - "columnsFrom": [ - "client_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "no action", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {} - }, - "session_upgrade_challenge": { - "name": "session_upgrade_challenge", - "columns": { - "id": { - "name": "id", - "type": "integer", - "primaryKey": true, - "notNull": true, - "autoincrement": false - }, - "session_id": { - "name": "session_id", - "type": "text", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "client_id": { - "name": "client_id", - "type": "integer", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "answer": { - "name": "answer", - "type": "text", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "allowed_ip": { - "name": "allowed_ip", - "type": "text", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "expires_at": { - "name": "expires_at", - "type": "integer", - "primaryKey": false, - "notNull": true, - "autoincrement": false - } - }, - "indexes": { - "session_upgrade_challenge_session_id_unique": { - "name": "session_upgrade_challenge_session_id_unique", - "columns": [ - "session_id" - ], - "isUnique": true - }, - "session_upgrade_challenge_answer_unique": { - "name": "session_upgrade_challenge_answer_unique", - "columns": [ - "answer" - ], - "isUnique": true - } - }, - "foreignKeys": { - "session_upgrade_challenge_session_id_session_id_fk": { - "name": "session_upgrade_challenge_session_id_session_id_fk", - "tableFrom": "session_upgrade_challenge", - "tableTo": "session", - "columnsFrom": [ - "session_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "no action", - "onUpdate": "no action" - }, - "session_upgrade_challenge_client_id_client_id_fk": { - "name": "session_upgrade_challenge_client_id_client_id_fk", - "tableFrom": "session_upgrade_challenge", - "tableTo": "client", - "columnsFrom": [ - "client_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "no action", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {} - }, - "user": { - "name": "user", - "columns": { - "id": { - "name": "id", - "type": "integer", - "primaryKey": true, - "notNull": true, - "autoincrement": true - }, - "email": { - "name": "email", - "type": "text", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "password": { - "name": "password", - "type": "text", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "nickname": { - "name": "nickname", - "type": "text", - "primaryKey": false, - "notNull": true, - "autoincrement": false - } - }, - "indexes": { - "user_email_unique": { - "name": "user_email_unique", - "columns": [ - "email" - ], - "isUnique": true - } - }, - "foreignKeys": {}, - "compositePrimaryKeys": {}, - "uniqueConstraints": {} - } - }, - "enums": {}, - "_meta": { - "schemas": {}, - "tables": {}, - "columns": {} - }, - "internal": { - "indexes": {} - } -} \ No newline at end of file diff --git a/drizzle/meta/_journal.json b/drizzle/meta/_journal.json deleted file mode 100644 index bdbdf66..0000000 --- a/drizzle/meta/_journal.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "version": "7", - "dialect": "sqlite", - "entries": [ - { - "idx": 0, - "version": "6", - "when": 1737219722656, - "tag": "0000_regular_the_watchers", - "breakpoints": true - } - ] -} \ No newline at end of file diff --git a/kysely.config.ts b/kysely.config.ts new file mode 100644 index 0000000..1fe5a76 --- /dev/null +++ b/kysely.config.ts @@ -0,0 +1,18 @@ +import { defineConfig } from "kysely-ctl"; +import { Pool } from "pg"; + +export default defineConfig({ + dialect: "pg", + dialectConfig: { + pool: new Pool({ + host: process.env.DATABASE_HOST, + port: process.env.DATABASE_PORT ? parseInt(process.env.DATABASE_PORT) : undefined, + user: process.env.DATABASE_USER, + password: process.env.DATABASE_PASSWORD, + database: process.env.DATABASE_NAME, + }), + }, + migrations: { + migrationFolder: "./src/lib/server/db/migrations", + }, +}); diff --git a/package.json b/package.json index 3a4adf2..8185f79 100644 --- a/package.json +++ b/package.json @@ -5,16 +5,14 @@ "type": "module", "scripts": { "dev": "vite dev", + "dev:db": "docker compose -f docker-compose.dev.yaml -p arkvault-dev up -d", "build": "vite build", "preview": "vite preview", "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", "format": "prettier --write .", "lint": "prettier --check . && eslint .", - "db:push": "drizzle-kit push", - "db:generate": "drizzle-kit generate", - "db:migrate": "drizzle-kit migrate", - "db:studio": "drizzle-kit studio" + "db:migrate": "kysely migrate" }, "devDependencies": { "@eslint/compat": "^1.2.4", @@ -26,10 +24,10 @@ "@types/file-saver": "^2.0.7", "@types/ms": "^0.7.34", "@types/node-schedule": "^2.1.7", + "@types/pg": "^8.11.10", "autoprefixer": "^10.4.20", "axios": "^1.7.9", "dexie": "^4.0.10", - "drizzle-kit": "^0.22.8", "eslint": "^9.17.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-svelte": "^2.46.1", @@ -38,6 +36,7 @@ "file-saver": "^2.0.5", "globals": "^15.14.0", "heic2any": "^0.0.4", + "kysely-ctl": "^0.10.1", "mime": "^4.0.6", "p-limit": "^6.2.0", "prettier": "^3.4.2", @@ -54,10 +53,10 @@ "dependencies": { "@fastify/busboy": "^3.1.1", "argon2": "^0.41.1", - "better-sqlite3": "^11.7.2", - "drizzle-orm": "^0.33.0", + "kysely": "^0.27.5", "ms": "^2.1.3", "node-schedule": "^2.1.1", + "pg": "^8.13.1", "uuid": "^11.0.4", "zod": "^3.24.1" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ae948dc..5092a35 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -14,18 +14,18 @@ importers: argon2: specifier: ^0.41.1 version: 0.41.1 - better-sqlite3: - specifier: ^11.7.2 - version: 11.7.2 - drizzle-orm: - specifier: ^0.33.0 - version: 0.33.0(@types/better-sqlite3@7.6.12)(better-sqlite3@11.7.2) + kysely: + specifier: ^0.27.5 + version: 0.27.5 ms: specifier: ^2.1.3 version: 2.1.3 node-schedule: specifier: ^2.1.1 version: 2.1.1 + pg: + specifier: ^8.13.1 + version: 8.13.1 uuid: specifier: ^11.0.4 version: 11.0.4 @@ -35,7 +35,7 @@ importers: devDependencies: '@eslint/compat': specifier: ^1.2.4 - version: 1.2.4(eslint@9.17.0(jiti@1.21.7)) + version: 1.2.4(eslint@9.17.0(jiti@2.4.2)) '@iconify-json/material-symbols': specifier: ^1.2.12 version: 1.2.12 @@ -60,6 +60,9 @@ importers: '@types/node-schedule': specifier: ^2.1.7 version: 2.1.7 + '@types/pg': + specifier: ^8.11.10 + version: 8.11.10 autoprefixer: specifier: ^10.4.20 version: 10.4.20(postcss@8.4.49) @@ -69,18 +72,15 @@ importers: dexie: specifier: ^4.0.10 version: 4.0.10 - drizzle-kit: - specifier: ^0.22.8 - version: 0.22.8 eslint: specifier: ^9.17.0 - version: 9.17.0(jiti@1.21.7) + version: 9.17.0(jiti@2.4.2) eslint-config-prettier: specifier: ^9.1.0 - version: 9.1.0(eslint@9.17.0(jiti@1.21.7)) + version: 9.1.0(eslint@9.17.0(jiti@2.4.2)) eslint-plugin-svelte: specifier: ^2.46.1 - version: 2.46.1(eslint@9.17.0(jiti@1.21.7))(svelte@5.17.1) + version: 2.46.1(eslint@9.17.0(jiti@2.4.2))(svelte@5.17.1) eslint-plugin-tailwindcss: specifier: ^3.17.5 version: 3.17.5(tailwindcss@3.4.17) @@ -96,6 +96,9 @@ importers: heic2any: specifier: ^0.0.4 version: 0.0.4 + kysely-ctl: + specifier: ^0.10.1 + version: 0.10.1(kysely@0.27.5) mime: specifier: ^4.0.6 version: 4.0.6 @@ -125,7 +128,7 @@ importers: version: 5.7.3 typescript-eslint: specifier: ^8.19.1 - version: 8.19.1(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.3) + version: 8.19.1(eslint@9.17.0(jiti@2.4.2))(typescript@5.7.3) unplugin-icons: specifier: ^0.22.0 version: 0.22.0(svelte@5.17.1) @@ -152,37 +155,17 @@ packages: '@antfu/utils@0.7.10': resolution: {integrity: sha512-+562v9k4aI80m1+VuMHehNJWLOFjBnXn3tdOitzD0il5b7smkSBal4+a3oKiQTbrwMmN/TBUMDvbdoWDehgOww==} - '@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==} - engines: {node: '>=12'} - cpu: [ppc64] - os: [aix] - '@esbuild/aix-ppc64@0.21.5': resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} engines: {node: '>=12'} cpu: [ppc64] os: [aix] - '@esbuild/android-arm64@0.18.20': - resolution: {integrity: sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==} - engines: {node: '>=12'} - cpu: [arm64] - os: [android] - - '@esbuild/android-arm64@0.19.12': - resolution: {integrity: sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==} - engines: {node: '>=12'} - cpu: [arm64] - os: [android] + '@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/android-arm64@0.21.5': resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} @@ -190,16 +173,10 @@ packages: cpu: [arm64] os: [android] - '@esbuild/android-arm@0.18.20': - resolution: {integrity: sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==} - engines: {node: '>=12'} - cpu: [arm] - os: [android] - - '@esbuild/android-arm@0.19.12': - resolution: {integrity: sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==} - engines: {node: '>=12'} - cpu: [arm] + '@esbuild/android-arm64@0.23.1': + resolution: {integrity: sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==} + engines: {node: '>=18'} + cpu: [arm64] os: [android] '@esbuild/android-arm@0.21.5': @@ -208,16 +185,10 @@ packages: cpu: [arm] os: [android] - '@esbuild/android-x64@0.18.20': - resolution: {integrity: sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==} - engines: {node: '>=12'} - cpu: [x64] - os: [android] - - '@esbuild/android-x64@0.19.12': - resolution: {integrity: sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==} - engines: {node: '>=12'} - cpu: [x64] + '@esbuild/android-arm@0.23.1': + resolution: {integrity: sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==} + engines: {node: '>=18'} + cpu: [arm] os: [android] '@esbuild/android-x64@0.21.5': @@ -226,17 +197,11 @@ packages: cpu: [x64] os: [android] - '@esbuild/darwin-arm64@0.18.20': - resolution: {integrity: sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==} - engines: {node: '>=12'} - cpu: [arm64] - os: [darwin] - - '@esbuild/darwin-arm64@0.19.12': - resolution: {integrity: sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==} - engines: {node: '>=12'} - cpu: [arm64] - os: [darwin] + '@esbuild/android-x64@0.23.1': + resolution: {integrity: sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] '@esbuild/darwin-arm64@0.21.5': resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} @@ -244,16 +209,10 @@ packages: cpu: [arm64] os: [darwin] - '@esbuild/darwin-x64@0.18.20': - resolution: {integrity: sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==} - engines: {node: '>=12'} - cpu: [x64] - os: [darwin] - - '@esbuild/darwin-x64@0.19.12': - resolution: {integrity: sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==} - engines: {node: '>=12'} - cpu: [x64] + '@esbuild/darwin-arm64@0.23.1': + resolution: {integrity: sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==} + engines: {node: '>=18'} + cpu: [arm64] os: [darwin] '@esbuild/darwin-x64@0.21.5': @@ -262,17 +221,11 @@ packages: cpu: [x64] os: [darwin] - '@esbuild/freebsd-arm64@0.18.20': - resolution: {integrity: sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==} - engines: {node: '>=12'} - cpu: [arm64] - os: [freebsd] - - '@esbuild/freebsd-arm64@0.19.12': - resolution: {integrity: sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==} - engines: {node: '>=12'} - cpu: [arm64] - os: [freebsd] + '@esbuild/darwin-x64@0.23.1': + resolution: {integrity: sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] '@esbuild/freebsd-arm64@0.21.5': resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} @@ -280,16 +233,10 @@ packages: cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-x64@0.18.20': - resolution: {integrity: sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==} - engines: {node: '>=12'} - cpu: [x64] - os: [freebsd] - - '@esbuild/freebsd-x64@0.19.12': - resolution: {integrity: sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==} - engines: {node: '>=12'} - cpu: [x64] + '@esbuild/freebsd-arm64@0.23.1': + resolution: {integrity: sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==} + engines: {node: '>=18'} + cpu: [arm64] os: [freebsd] '@esbuild/freebsd-x64@0.21.5': @@ -298,17 +245,11 @@ packages: cpu: [x64] os: [freebsd] - '@esbuild/linux-arm64@0.18.20': - resolution: {integrity: sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==} - engines: {node: '>=12'} - cpu: [arm64] - os: [linux] - - '@esbuild/linux-arm64@0.19.12': - resolution: {integrity: sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==} - engines: {node: '>=12'} - cpu: [arm64] - os: [linux] + '@esbuild/freebsd-x64@0.23.1': + resolution: {integrity: sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] '@esbuild/linux-arm64@0.21.5': resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} @@ -316,16 +257,10 @@ packages: cpu: [arm64] os: [linux] - '@esbuild/linux-arm@0.18.20': - resolution: {integrity: sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==} - engines: {node: '>=12'} - cpu: [arm] - os: [linux] - - '@esbuild/linux-arm@0.19.12': - resolution: {integrity: sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==} - engines: {node: '>=12'} - cpu: [arm] + '@esbuild/linux-arm64@0.23.1': + resolution: {integrity: sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==} + engines: {node: '>=18'} + cpu: [arm64] os: [linux] '@esbuild/linux-arm@0.21.5': @@ -334,16 +269,10 @@ packages: cpu: [arm] os: [linux] - '@esbuild/linux-ia32@0.18.20': - resolution: {integrity: sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==} - engines: {node: '>=12'} - cpu: [ia32] - os: [linux] - - '@esbuild/linux-ia32@0.19.12': - resolution: {integrity: sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==} - engines: {node: '>=12'} - cpu: [ia32] + '@esbuild/linux-arm@0.23.1': + resolution: {integrity: sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==} + engines: {node: '>=18'} + cpu: [arm] os: [linux] '@esbuild/linux-ia32@0.21.5': @@ -352,16 +281,10 @@ packages: cpu: [ia32] os: [linux] - '@esbuild/linux-loong64@0.18.20': - resolution: {integrity: sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==} - engines: {node: '>=12'} - cpu: [loong64] - os: [linux] - - '@esbuild/linux-loong64@0.19.12': - resolution: {integrity: sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==} - engines: {node: '>=12'} - cpu: [loong64] + '@esbuild/linux-ia32@0.23.1': + resolution: {integrity: sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==} + engines: {node: '>=18'} + cpu: [ia32] os: [linux] '@esbuild/linux-loong64@0.21.5': @@ -370,16 +293,10 @@ packages: cpu: [loong64] os: [linux] - '@esbuild/linux-mips64el@0.18.20': - resolution: {integrity: sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==} - engines: {node: '>=12'} - cpu: [mips64el] - os: [linux] - - '@esbuild/linux-mips64el@0.19.12': - resolution: {integrity: sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==} - engines: {node: '>=12'} - cpu: [mips64el] + '@esbuild/linux-loong64@0.23.1': + resolution: {integrity: sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==} + engines: {node: '>=18'} + cpu: [loong64] os: [linux] '@esbuild/linux-mips64el@0.21.5': @@ -388,16 +305,10 @@ packages: cpu: [mips64el] os: [linux] - '@esbuild/linux-ppc64@0.18.20': - resolution: {integrity: sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==} - engines: {node: '>=12'} - cpu: [ppc64] - os: [linux] - - '@esbuild/linux-ppc64@0.19.12': - resolution: {integrity: sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==} - engines: {node: '>=12'} - cpu: [ppc64] + '@esbuild/linux-mips64el@0.23.1': + resolution: {integrity: sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==} + engines: {node: '>=18'} + cpu: [mips64el] os: [linux] '@esbuild/linux-ppc64@0.21.5': @@ -406,16 +317,10 @@ packages: cpu: [ppc64] os: [linux] - '@esbuild/linux-riscv64@0.18.20': - resolution: {integrity: sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==} - engines: {node: '>=12'} - cpu: [riscv64] - os: [linux] - - '@esbuild/linux-riscv64@0.19.12': - resolution: {integrity: sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==} - engines: {node: '>=12'} - cpu: [riscv64] + '@esbuild/linux-ppc64@0.23.1': + resolution: {integrity: sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==} + engines: {node: '>=18'} + cpu: [ppc64] os: [linux] '@esbuild/linux-riscv64@0.21.5': @@ -424,16 +329,10 @@ packages: cpu: [riscv64] os: [linux] - '@esbuild/linux-s390x@0.18.20': - resolution: {integrity: sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==} - engines: {node: '>=12'} - cpu: [s390x] - os: [linux] - - '@esbuild/linux-s390x@0.19.12': - resolution: {integrity: sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==} - engines: {node: '>=12'} - cpu: [s390x] + '@esbuild/linux-riscv64@0.23.1': + resolution: {integrity: sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==} + engines: {node: '>=18'} + cpu: [riscv64] os: [linux] '@esbuild/linux-s390x@0.21.5': @@ -442,16 +341,10 @@ packages: cpu: [s390x] os: [linux] - '@esbuild/linux-x64@0.18.20': - resolution: {integrity: sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==} - engines: {node: '>=12'} - cpu: [x64] - os: [linux] - - '@esbuild/linux-x64@0.19.12': - resolution: {integrity: sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==} - engines: {node: '>=12'} - cpu: [x64] + '@esbuild/linux-s390x@0.23.1': + resolution: {integrity: sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==} + engines: {node: '>=18'} + cpu: [s390x] os: [linux] '@esbuild/linux-x64@0.21.5': @@ -460,17 +353,11 @@ packages: cpu: [x64] os: [linux] - '@esbuild/netbsd-x64@0.18.20': - resolution: {integrity: sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==} - engines: {node: '>=12'} + '@esbuild/linux-x64@0.23.1': + resolution: {integrity: sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==} + engines: {node: '>=18'} cpu: [x64] - os: [netbsd] - - '@esbuild/netbsd-x64@0.19.12': - resolution: {integrity: sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==} - engines: {node: '>=12'} - cpu: [x64] - os: [netbsd] + os: [linux] '@esbuild/netbsd-x64@0.21.5': resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} @@ -478,16 +365,16 @@ packages: cpu: [x64] os: [netbsd] - '@esbuild/openbsd-x64@0.18.20': - resolution: {integrity: sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==} - engines: {node: '>=12'} + '@esbuild/netbsd-x64@0.23.1': + resolution: {integrity: sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==} + engines: {node: '>=18'} cpu: [x64] - os: [openbsd] + os: [netbsd] - '@esbuild/openbsd-x64@0.19.12': - resolution: {integrity: sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==} - engines: {node: '>=12'} - cpu: [x64] + '@esbuild/openbsd-arm64@0.23.1': + resolution: {integrity: sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==} + engines: {node: '>=18'} + cpu: [arm64] os: [openbsd] '@esbuild/openbsd-x64@0.21.5': @@ -496,17 +383,11 @@ packages: cpu: [x64] os: [openbsd] - '@esbuild/sunos-x64@0.18.20': - resolution: {integrity: sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==} - engines: {node: '>=12'} + '@esbuild/openbsd-x64@0.23.1': + resolution: {integrity: sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==} + engines: {node: '>=18'} cpu: [x64] - os: [sunos] - - '@esbuild/sunos-x64@0.19.12': - resolution: {integrity: sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==} - engines: {node: '>=12'} - cpu: [x64] - os: [sunos] + os: [openbsd] '@esbuild/sunos-x64@0.21.5': resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} @@ -514,17 +395,11 @@ packages: cpu: [x64] os: [sunos] - '@esbuild/win32-arm64@0.18.20': - resolution: {integrity: sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==} - engines: {node: '>=12'} - cpu: [arm64] - os: [win32] - - '@esbuild/win32-arm64@0.19.12': - resolution: {integrity: sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==} - engines: {node: '>=12'} - cpu: [arm64] - os: [win32] + '@esbuild/sunos-x64@0.23.1': + resolution: {integrity: sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] '@esbuild/win32-arm64@0.21.5': resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} @@ -532,16 +407,10 @@ packages: cpu: [arm64] os: [win32] - '@esbuild/win32-ia32@0.18.20': - resolution: {integrity: sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==} - engines: {node: '>=12'} - cpu: [ia32] - os: [win32] - - '@esbuild/win32-ia32@0.19.12': - resolution: {integrity: sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==} - engines: {node: '>=12'} - cpu: [ia32] + '@esbuild/win32-arm64@0.23.1': + resolution: {integrity: sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==} + engines: {node: '>=18'} + cpu: [arm64] os: [win32] '@esbuild/win32-ia32@0.21.5': @@ -550,16 +419,10 @@ packages: cpu: [ia32] os: [win32] - '@esbuild/win32-x64@0.18.20': - resolution: {integrity: sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==} - engines: {node: '>=12'} - cpu: [x64] - os: [win32] - - '@esbuild/win32-x64@0.19.12': - resolution: {integrity: sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==} - engines: {node: '>=12'} - cpu: [x64] + '@esbuild/win32-ia32@0.23.1': + resolution: {integrity: sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==} + engines: {node: '>=18'} + cpu: [ia32] os: [win32] '@esbuild/win32-x64@0.21.5': @@ -568,6 +431,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] + '@eslint-community/eslint-utils@4.4.1': resolution: {integrity: sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -872,6 +741,9 @@ packages: '@types/node@22.10.5': resolution: {integrity: sha512-F8Q+SeGimwOo86fiovQh8qiXfFEh2/ocYv7tU5pJ3EXMSSxk1Joj5wefpFK2fHTf/N6HKGSxIDBT9f3gCxXPkQ==} + '@types/pg@8.11.10': + resolution: {integrity: sha512-LczQUW4dbOQzsH2RQ5qoeJ6qJPdrcM/DcMLoqWQkMLMsq83J5lAX3LXjdkWdpscFy67JSOWDnh7Ny/sPFykmkg==} + '@types/resolve@1.20.2': resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==} @@ -1001,22 +873,10 @@ packages: balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} - base64-js@1.5.1: - resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} - - better-sqlite3@11.7.2: - resolution: {integrity: sha512-10a57cHVDmfNQS4jrZ9AH2t+2ekzYh5Rhbcnb4ytpmYweoLdogDmyTt5D+hLiY9b44Mx9foowb/4iXBTO2yP3Q==} - binary-extensions@2.3.0: resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} engines: {node: '>=8'} - bindings@1.5.0: - resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==} - - bl@4.1.0: - resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} - brace-expansion@1.1.11: resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} @@ -1032,11 +892,13 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true - buffer-from@1.1.2: - resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} - - buffer@5.7.1: - resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} + c12@2.0.1: + resolution: {integrity: sha512-Z4JgsKXHG37C6PYUtIxCfLJZvo6FyhHJoClwwb9ftUkLpPSkuYqn6Tr+vnaN8hymm0kIbcg6Ey3kv/Q71k5w/A==} + peerDependencies: + magicast: ^0.3.5 + peerDependenciesMeta: + magicast: + optional: true callsites@3.1.0: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} @@ -1061,8 +923,12 @@ packages: resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} engines: {node: '>= 14.16.0'} - chownr@1.1.4: - resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==} + chownr@2.0.0: + resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} + engines: {node: '>=10'} + + citty@0.1.6: + resolution: {integrity: sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==} clsx@2.1.1: resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} @@ -1092,6 +958,10 @@ packages: confbox@0.1.8: resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==} + consola@3.4.0: + resolution: {integrity: sha512-EiPU8G6dQG0GFHNR8ljnZFki/8a+cQwEQ+7wpxdChl02Q8HXlwEZWD5lqAF8vC2sEC3Tehr8hy7vErz88LHyUA==} + engines: {node: ^14.18.0 || >=16.10.0} + cookie@0.6.0: resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==} engines: {node: '>= 0.6'} @@ -1118,14 +988,6 @@ packages: supports-color: optional: true - decompress-response@6.0.0: - resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} - engines: {node: '>=10'} - - deep-extend@0.6.0: - resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} - engines: {node: '>=4.0.0'} - deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} @@ -1133,13 +995,15 @@ packages: resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} engines: {node: '>=0.10.0'} + defu@6.1.4: + resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==} + delayed-stream@1.0.0: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} engines: {node: '>=0.4.0'} - detect-libc@2.0.3: - resolution: {integrity: sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==} - engines: {node: '>=8'} + destr@2.0.3: + resolution: {integrity: sha512-2N3BOUU4gYMpTP24s5rF5iP7BDr7uNTCs4ozw3kf/eKfvWSIu93GEBi5m427YoyJoeOzQ5smuu4nNAPGb8idSQ==} devalue@5.1.1: resolution: {integrity: sha512-maua5KUiapvEwiEAe+XnlZ3Rh0GD+qI1J/nb9vrJc3muPXvcF/8gXYTWF76+5DAqHyDUtOIImEuo0YKE9mshVw==} @@ -1153,98 +1017,9 @@ packages: dlv@1.1.3: resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} - drizzle-kit@0.22.8: - resolution: {integrity: sha512-VjI4wsJjk3hSqHSa3TwBf+uvH6M6pRHyxyoVbt935GUzP9tUR/BRZ+MhEJNgryqbzN2Za1KP0eJMTgKEPsalYQ==} - hasBin: true - - drizzle-orm@0.33.0: - resolution: {integrity: sha512-SHy72R2Rdkz0LEq0PSG/IdvnT3nGiWuRk+2tXZQ90GVq/XQhpCzu/EFT3V2rox+w8MlkBQxifF8pCStNYnERfA==} - peerDependencies: - '@aws-sdk/client-rds-data': '>=3' - '@cloudflare/workers-types': '>=3' - '@electric-sql/pglite': '>=0.1.1' - '@libsql/client': '*' - '@neondatabase/serverless': '>=0.1' - '@op-engineering/op-sqlite': '>=2' - '@opentelemetry/api': ^1.4.1 - '@planetscale/database': '>=1' - '@prisma/client': '*' - '@tidbcloud/serverless': '*' - '@types/better-sqlite3': '*' - '@types/pg': '*' - '@types/react': '>=18' - '@types/sql.js': '*' - '@vercel/postgres': '>=0.8.0' - '@xata.io/client': '*' - better-sqlite3: '>=7' - bun-types: '*' - expo-sqlite: '>=13.2.0' - knex: '*' - kysely: '*' - mysql2: '>=2' - pg: '>=8' - postgres: '>=3' - prisma: '*' - react: '>=18' - sql.js: '>=1' - sqlite3: '>=5' - peerDependenciesMeta: - '@aws-sdk/client-rds-data': - optional: true - '@cloudflare/workers-types': - optional: true - '@electric-sql/pglite': - optional: true - '@libsql/client': - optional: true - '@neondatabase/serverless': - optional: true - '@op-engineering/op-sqlite': - optional: true - '@opentelemetry/api': - optional: true - '@planetscale/database': - optional: true - '@prisma/client': - optional: true - '@tidbcloud/serverless': - optional: true - '@types/better-sqlite3': - optional: true - '@types/pg': - optional: true - '@types/react': - optional: true - '@types/sql.js': - optional: true - '@vercel/postgres': - optional: true - '@xata.io/client': - optional: true - better-sqlite3: - optional: true - bun-types: - optional: true - expo-sqlite: - optional: true - knex: - optional: true - kysely: - optional: true - mysql2: - optional: true - pg: - optional: true - postgres: - optional: true - prisma: - optional: true - react: - optional: true - sql.js: - optional: true - sqlite3: - optional: true + dotenv@16.4.7: + resolution: {integrity: sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==} + engines: {node: '>=12'} eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} @@ -1258,29 +1033,16 @@ packages: emoji-regex@9.2.2: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} - end-of-stream@1.4.4: - resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} - - esbuild-register@3.6.0: - resolution: {integrity: sha512-H2/S7Pm8a9CL1uhp9OvjwrBh5Pvx0H8qVOxNu8Wed9Y7qv56MPtq+GGM8RJpq6glYJn9Wspr8uw7l55uyinNeg==} - peerDependencies: - esbuild: '>=0.12 <1' - - esbuild@0.18.20: - resolution: {integrity: sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==} - engines: {node: '>=12'} - hasBin: true - - esbuild@0.19.12: - resolution: {integrity: sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==} - engines: {node: '>=12'} - hasBin: true - esbuild@0.21.5: resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} engines: {node: '>=12'} hasBin: true + esbuild@0.23.1: + resolution: {integrity: sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==} + engines: {node: '>=18'} + hasBin: true + escalade@3.2.0: resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} engines: {node: '>=6'} @@ -1376,13 +1138,13 @@ packages: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} + execa@8.0.1: + resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} + engines: {node: '>=16.17'} + exifreader@4.26.0: resolution: {integrity: sha512-nNN9B0oaXTOpArdnIdJBAro2Sa620m7wMjMA5Xy1rcua0EYHVjzGKM5syBOWDqIG2Qay6Pes/5FOdj65hvZ9Vw==} - expand-template@2.0.3: - resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==} - engines: {node: '>=6'} - fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} @@ -1414,9 +1176,6 @@ packages: file-saver@2.0.5: resolution: {integrity: sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==} - file-uri-to-path@1.0.0: - resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==} - fill-range@7.1.1: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} @@ -1452,8 +1211,9 @@ packages: fraction.js@4.3.7: resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==} - fs-constants@1.0.0: - resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} + fs-minipass@2.1.0: + resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==} + engines: {node: '>= 8'} fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} @@ -1463,11 +1223,16 @@ packages: function-bind@1.1.2: resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + get-stream@8.0.1: + resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==} + engines: {node: '>=16'} + get-tsconfig@4.8.1: resolution: {integrity: sha512-k9PN+cFBmaLWtVz29SkUoqU5O0slLuHJXt/2P+tMVFT+phsSGXGkp9t3rQIqdz0e+06EHNGs3oM6ZX1s2zHxRg==} - github-from-package@0.0.0: - resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==} + giget@1.2.3: + resolution: {integrity: sha512-8EHPljDvs7qKykr6uw8b+lqLiUc/vUg+KVTI0uND4s63TdsZM2Xus3mflvF0DDG9SiM4RlCkFGL+7aAjRmV7KA==} + hasBin: true glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} @@ -1509,8 +1274,9 @@ packages: heic2any@0.0.4: resolution: {integrity: sha512-3lLnZiDELfabVH87htnRolZ2iehX9zwpRyGNz22GKXIu0fznlblf0/ftppXKNqS26dqFSeqfIBhAmAj/uSp0cA==} - ieee754@1.2.1: - resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + human-signals@5.0.0: + resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} + engines: {node: '>=16.17.0'} ignore@5.3.2: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} @@ -1527,12 +1293,6 @@ packages: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} - inherits@2.0.4: - resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} - - ini@1.3.8: - resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} - is-binary-path@2.1.0: resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} engines: {node: '>=8'} @@ -1566,6 +1326,10 @@ packages: is-reference@3.0.3: resolution: {integrity: sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==} + is-stream@3.0.0: + resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} @@ -1576,6 +1340,10 @@ packages: resolution: {integrity: sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==} hasBin: true + jiti@2.4.2: + resolution: {integrity: sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==} + hasBin: true + js-yaml@4.1.0: resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} hasBin: true @@ -1602,6 +1370,21 @@ packages: kolorist@1.8.0: resolution: {integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==} + kysely-ctl@0.10.1: + resolution: {integrity: sha512-tB2mGV/MbfaQC6Lo582Rs2OdtfX23ueWDscCSDT42Iy8pYHYbhMy9ncXU35ee8LQz4BO2apQihyY8rDProP+9w==} + engines: {node: '>=18'} + hasBin: true + peerDependencies: + kysely: '>=0.18.1 <0.28.0' + kysely-postgres-js: ^2 + peerDependenciesMeta: + kysely-postgres-js: + optional: true + + kysely@0.27.5: + resolution: {integrity: sha512-s7hZHcQeSNKpzCkHRm8yA+0JPLjncSWnjb+2TIElwS2JAqYr+Kv3Ess+9KFfJS0C1xcQ1i9NkNHpWwCYpHMWsA==} + engines: {node: '>=14.0.0'} + levn@0.4.1: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} @@ -1644,6 +1427,9 @@ packages: magic-string@0.30.17: resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} + merge-stream@2.0.0: + resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} + merge2@1.4.1: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} @@ -1665,9 +1451,9 @@ packages: engines: {node: '>=16'} hasBin: true - mimic-response@3.1.0: - resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} - engines: {node: '>=10'} + mimic-fn@4.0.0: + resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} + engines: {node: '>=12'} minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} @@ -1676,15 +1462,26 @@ packages: resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} engines: {node: '>=16 || 14 >=14.17'} - minimist@1.2.8: - resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + minipass@3.3.6: + resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==} + engines: {node: '>=8'} + + minipass@5.0.0: + resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==} + engines: {node: '>=8'} minipass@7.1.2: resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} engines: {node: '>=16 || 14 >=14.17'} - mkdirp-classic@0.5.3: - resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==} + minizlib@2.1.2: + resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} + engines: {node: '>= 8'} + + mkdirp@1.0.4: + resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} + engines: {node: '>=10'} + hasBin: true mlly@1.7.3: resolution: {integrity: sha512-xUsx5n/mN0uQf4V548PKQ+YShA4/IW0KI1dZhrNrPCLG+xizETbHTkOa1f8/xut9JRPp8kQuMnz0oqwkTiLo/A==} @@ -1708,20 +1505,16 @@ packages: engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true - napi-build-utils@1.0.2: - resolution: {integrity: sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==} - natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} - node-abi@3.71.0: - resolution: {integrity: sha512-SZ40vRiy/+wRTf21hxkkEjPJZpARzUMVcJoQse2EF8qkUWbbO2z7vd5oA/H6bVH6SZQ5STGcu0KRDS7biNRfxw==} - engines: {node: '>=10'} - node-addon-api@8.3.0: resolution: {integrity: sha512-8VOpLHFrOQlAH+qA0ZzuGRlALRA6/LVh8QJldbrC4DY0hXoMP0l4Acq8TzFC018HztWiRqyCEj2aTWY2UvnJUg==} engines: {node: ^18 || ^20 || >= 21} + node-fetch-native@1.6.4: + resolution: {integrity: sha512-IhOigYzAKHd244OC0JIMIUrjzctirCmPkaIfhDeGcEETWof5zKYUW7e7MYvChGWh/4CJeXEgsRyGzuF334rOOQ==} + node-gyp-build@4.8.4: resolution: {integrity: sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==} hasBin: true @@ -1741,6 +1534,20 @@ packages: resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==} engines: {node: '>=0.10.0'} + npm-run-path@5.3.0: + resolution: {integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + nypm@0.3.12: + resolution: {integrity: sha512-D3pzNDWIvgA+7IORhD/IuWzEk4uXv6GsgOxiid4UU3h9oq5IqV1KtPDi63n4sZJ/xcWlr88c0QM2RgN5VbOhFA==} + engines: {node: ^14.16.0 || >=16.10.0} + hasBin: true + + nypm@0.4.1: + resolution: {integrity: sha512-1b9mihliBh8UCcKtcGRu//G50iHpjxIQVUqkdhPT/SDVE7KdJKoHXLS0heuYTQCx95dFqiyUbXZB9r8ikn+93g==} + engines: {node: ^14.16.0 || >=16.10.0} + hasBin: true + object-assign@4.1.1: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} @@ -1749,8 +1556,18 @@ packages: resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} engines: {node: '>= 6'} - once@1.4.0: - resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + obuf@1.1.2: + resolution: {integrity: sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==} + + ofetch@1.4.1: + resolution: {integrity: sha512-QZj2DfGplQAr2oj9KzceK9Hwz6Whxazmn85yYeVuS3u9XTMOGMRx0kO95MQ+vLsj/S/NwBDMMLU5hpxvI6Tklw==} + + ohash@1.1.4: + resolution: {integrity: sha512-FlDryZAahJmEF3VR3w1KogSEdWX3WhA5GPakFx4J81kEAiHyLMpdLLElS8n8dfNadMgAne/MywcvmogzscVt4g==} + + onetime@6.0.0: + resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} + engines: {node: '>=12'} optionator@0.9.4: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} @@ -1786,6 +1603,10 @@ packages: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} + path-key@4.0.0: + resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} + engines: {node: '>=12'} + path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} @@ -1796,6 +1617,51 @@ packages: pathe@1.1.2: resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} + perfect-debounce@1.0.0: + resolution: {integrity: sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==} + + pg-cloudflare@1.1.1: + resolution: {integrity: sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==} + + pg-connection-string@2.7.0: + resolution: {integrity: sha512-PI2W9mv53rXJQEOb8xNR8lH7Hr+EKa6oJa38zsK0S/ky2er16ios1wLKhZyxzD7jUReiWokc9WK5nxSnC7W1TA==} + + pg-int8@1.0.1: + resolution: {integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==} + engines: {node: '>=4.0.0'} + + pg-numeric@1.0.2: + resolution: {integrity: sha512-BM/Thnrw5jm2kKLE5uJkXqqExRUY/toLHda65XgFTBTFYZyopbKjBe29Ii3RbkvlsMoFwD+tHeGaCjjv0gHlyw==} + engines: {node: '>=4'} + + pg-pool@3.7.0: + resolution: {integrity: sha512-ZOBQForurqh4zZWjrgSwwAtzJ7QiRX0ovFkZr2klsen3Nm0aoh33Ls0fzfv3imeH/nw/O27cjdz5kzYJfeGp/g==} + peerDependencies: + pg: '>=8.0' + + pg-protocol@1.7.0: + resolution: {integrity: sha512-hTK/mE36i8fDDhgDFjy6xNOG+LCorxLG3WO17tku+ij6sVHXh1jQUJ8hYAnRhNla4QVD2H8er/FOjc/+EgC6yQ==} + + pg-types@2.2.0: + resolution: {integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==} + engines: {node: '>=4'} + + pg-types@4.0.2: + resolution: {integrity: sha512-cRL3JpS3lKMGsKaWndugWQoLOCoP+Cic8oseVcbr0qhPzYD5DWXK+RZ9LY9wxRf7RQia4SCwQlXk0q6FCPrVng==} + engines: {node: '>=10'} + + pg@8.13.1: + resolution: {integrity: sha512-OUir1A0rPNZlX//c7ksiu7crsGZTKSOXJPgtNiHGIlC9H0lO+NC6ZDYksSgBYY/thSWhnSRBv8w1lieNNGATNQ==} + engines: {node: '>= 8.0.0'} + peerDependencies: + pg-native: '>=3.0.1' + peerDependenciesMeta: + pg-native: + optional: true + + pgpass@1.0.5: + resolution: {integrity: sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==} + picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} @@ -1883,10 +1749,40 @@ packages: resolution: {integrity: sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==} engines: {node: ^10 || ^12 || >=14} - prebuild-install@7.1.2: - resolution: {integrity: sha512-UnNke3IQb6sgarcZIDU3gbMeTp/9SSU1DAIkil7PrqG1vZlBtY5msYccSKSHDqa3hNg436IXK+SNImReuA1wEQ==} - engines: {node: '>=10'} - hasBin: true + postgres-array@2.0.0: + resolution: {integrity: sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==} + engines: {node: '>=4'} + + postgres-array@3.0.2: + resolution: {integrity: sha512-6faShkdFugNQCLwucjPcY5ARoW1SlbnrZjmGl0IrrqewpvxvhSLHimCVzqeuULCbG0fQv7Dtk1yDbG3xv7Veog==} + engines: {node: '>=12'} + + postgres-bytea@1.0.0: + resolution: {integrity: sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==} + engines: {node: '>=0.10.0'} + + postgres-bytea@3.0.0: + resolution: {integrity: sha512-CNd4jim9RFPkObHSjVHlVrxoVQXz7quwNFpz7RY1okNNme49+sVyiTvTRobiLV548Hx/hb1BG+iE7h9493WzFw==} + engines: {node: '>= 6'} + + postgres-date@1.0.7: + resolution: {integrity: sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==} + engines: {node: '>=0.10.0'} + + postgres-date@2.1.0: + resolution: {integrity: sha512-K7Juri8gtgXVcDfZttFKVmhglp7epKb1K4pgrkLxehjqkrgPhfG6OO8LHLkfaqkbpjNRnra018XwAr1yQFWGcA==} + engines: {node: '>=12'} + + postgres-interval@1.2.0: + resolution: {integrity: sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==} + engines: {node: '>=0.10.0'} + + postgres-interval@3.0.0: + resolution: {integrity: sha512-BSNDnbyZCXSxgA+1f5UU2GmwhoI0aU5yMxRGO8CdFEcY2BQF9xm/7MqKnYoM1nJDk8nONNWDk9WeSmePFhQdlw==} + engines: {node: '>=12'} + + postgres-range@1.1.4: + resolution: {integrity: sha512-i/hbxIE9803Alj/6ytL7UHQxRvZkI9O4Sy+J3HGc4F4oo/2eQAjTSNJ0bfxyse3bH0nuVesCk+3IRLaMtG3H6w==} prelude-ls@1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} @@ -1961,9 +1857,6 @@ packages: proxy-from-env@1.1.0: resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} - pump@3.0.2: - resolution: {integrity: sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==} - punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} @@ -1971,17 +1864,12 @@ packages: queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} - rc@1.2.8: - resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} - hasBin: true + rc9@2.1.2: + resolution: {integrity: sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==} read-cache@1.0.0: resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==} - readable-stream@3.6.2: - resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} - engines: {node: '>= 6'} - readdirp@3.6.0: resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} engines: {node: '>=8.10.0'} @@ -2018,9 +1906,6 @@ packages: resolution: {integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==} engines: {node: '>=6'} - safe-buffer@5.2.1: - resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} - semver@7.6.3: resolution: {integrity: sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==} engines: {node: '>=10'} @@ -2041,12 +1926,6 @@ packages: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} - simple-concat@1.0.1: - resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==} - - simple-get@4.0.1: - resolution: {integrity: sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==} - sirv@3.0.0: resolution: {integrity: sha512-BPwJGUeDaDCHihkORDchNyyTvWFhcusy1XMmhEVTQTwGeybFbp8YEmB+njbPnth1FibULBSBVwCQni25XlCUDg==} engines: {node: '>=18'} @@ -2058,12 +1937,12 @@ packages: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} - source-map-support@0.5.21: - resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} + split2@4.2.0: + resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} + engines: {node: '>= 10.x'} - source-map@0.6.1: - resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} - engines: {node: '>=0.10.0'} + std-env@3.8.0: + resolution: {integrity: sha512-Bc3YwwCB+OzldMxOXJIIvC6cPRWr/LxOp48CdQTOkPyk/t4JWWJbrilwBd7RJzKV8QW7tJkcgAmeuLLJugl5/w==} string-width@4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} @@ -2073,9 +1952,6 @@ packages: resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} engines: {node: '>=12'} - string_decoder@1.3.0: - resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} - strip-ansi@6.0.1: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} @@ -2084,9 +1960,9 @@ packages: resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} engines: {node: '>=12'} - strip-json-comments@2.0.1: - resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} - engines: {node: '>=0.10.0'} + strip-final-newline@3.0.0: + resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} + engines: {node: '>=12'} strip-json-comments@3.1.1: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} @@ -2131,12 +2007,9 @@ packages: engines: {node: '>=14.0.0'} hasBin: true - tar-fs@2.1.1: - resolution: {integrity: sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==} - - tar-stream@2.2.0: - resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} - engines: {node: '>=6'} + tar@6.2.1: + resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==} + engines: {node: '>=10'} thenify-all@1.6.0: resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} @@ -2168,8 +2041,10 @@ packages: ts-interface-checker@0.1.13: resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} - tunnel-agent@0.6.0: - resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==} + tsx@4.19.2: + resolution: {integrity: sha512-pOUl6Vo2LUq/bSa8S5q7b91cgNSjctn9ugq/+Mvow99qW6x/UZYwzxy/3NmqoT66eHYfCVvFvACC58UBPFf28g==} + engines: {node: '>=18.0.0'} + hasBin: true type-check@0.4.0: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} @@ -2295,8 +2170,12 @@ packages: resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} engines: {node: '>=12'} - wrappy@1.0.2: - resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + xtend@4.0.2: + resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} + engines: {node: '>=0.4'} + + yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} yaml@1.10.2: resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} @@ -2342,230 +2221,157 @@ snapshots: '@antfu/utils@0.7.10': {} - '@esbuild-kit/core-utils@3.3.2': - dependencies: - esbuild: 0.18.20 - source-map-support: 0.5.21 - - '@esbuild-kit/esm-loader@2.6.5': - dependencies: - '@esbuild-kit/core-utils': 3.3.2 - get-tsconfig: 4.8.1 - - '@esbuild/aix-ppc64@0.19.12': - optional: true - '@esbuild/aix-ppc64@0.21.5': optional: true - '@esbuild/android-arm64@0.18.20': - optional: true - - '@esbuild/android-arm64@0.19.12': + '@esbuild/aix-ppc64@0.23.1': optional: true '@esbuild/android-arm64@0.21.5': optional: true - '@esbuild/android-arm@0.18.20': - optional: true - - '@esbuild/android-arm@0.19.12': + '@esbuild/android-arm64@0.23.1': optional: true '@esbuild/android-arm@0.21.5': optional: true - '@esbuild/android-x64@0.18.20': - optional: true - - '@esbuild/android-x64@0.19.12': + '@esbuild/android-arm@0.23.1': optional: true '@esbuild/android-x64@0.21.5': optional: true - '@esbuild/darwin-arm64@0.18.20': - optional: true - - '@esbuild/darwin-arm64@0.19.12': + '@esbuild/android-x64@0.23.1': optional: true '@esbuild/darwin-arm64@0.21.5': optional: true - '@esbuild/darwin-x64@0.18.20': - optional: true - - '@esbuild/darwin-x64@0.19.12': + '@esbuild/darwin-arm64@0.23.1': optional: true '@esbuild/darwin-x64@0.21.5': optional: true - '@esbuild/freebsd-arm64@0.18.20': - optional: true - - '@esbuild/freebsd-arm64@0.19.12': + '@esbuild/darwin-x64@0.23.1': optional: true '@esbuild/freebsd-arm64@0.21.5': optional: true - '@esbuild/freebsd-x64@0.18.20': - optional: true - - '@esbuild/freebsd-x64@0.19.12': + '@esbuild/freebsd-arm64@0.23.1': optional: true '@esbuild/freebsd-x64@0.21.5': optional: true - '@esbuild/linux-arm64@0.18.20': - optional: true - - '@esbuild/linux-arm64@0.19.12': + '@esbuild/freebsd-x64@0.23.1': optional: true '@esbuild/linux-arm64@0.21.5': optional: true - '@esbuild/linux-arm@0.18.20': - optional: true - - '@esbuild/linux-arm@0.19.12': + '@esbuild/linux-arm64@0.23.1': optional: true '@esbuild/linux-arm@0.21.5': optional: true - '@esbuild/linux-ia32@0.18.20': - optional: true - - '@esbuild/linux-ia32@0.19.12': + '@esbuild/linux-arm@0.23.1': optional: true '@esbuild/linux-ia32@0.21.5': optional: true - '@esbuild/linux-loong64@0.18.20': - optional: true - - '@esbuild/linux-loong64@0.19.12': + '@esbuild/linux-ia32@0.23.1': optional: true '@esbuild/linux-loong64@0.21.5': optional: true - '@esbuild/linux-mips64el@0.18.20': - optional: true - - '@esbuild/linux-mips64el@0.19.12': + '@esbuild/linux-loong64@0.23.1': optional: true '@esbuild/linux-mips64el@0.21.5': optional: true - '@esbuild/linux-ppc64@0.18.20': - optional: true - - '@esbuild/linux-ppc64@0.19.12': + '@esbuild/linux-mips64el@0.23.1': optional: true '@esbuild/linux-ppc64@0.21.5': optional: true - '@esbuild/linux-riscv64@0.18.20': - optional: true - - '@esbuild/linux-riscv64@0.19.12': + '@esbuild/linux-ppc64@0.23.1': optional: true '@esbuild/linux-riscv64@0.21.5': optional: true - '@esbuild/linux-s390x@0.18.20': - optional: true - - '@esbuild/linux-s390x@0.19.12': + '@esbuild/linux-riscv64@0.23.1': optional: true '@esbuild/linux-s390x@0.21.5': optional: true - '@esbuild/linux-x64@0.18.20': - optional: true - - '@esbuild/linux-x64@0.19.12': + '@esbuild/linux-s390x@0.23.1': optional: true '@esbuild/linux-x64@0.21.5': optional: true - '@esbuild/netbsd-x64@0.18.20': - optional: true - - '@esbuild/netbsd-x64@0.19.12': + '@esbuild/linux-x64@0.23.1': optional: true '@esbuild/netbsd-x64@0.21.5': optional: true - '@esbuild/openbsd-x64@0.18.20': + '@esbuild/netbsd-x64@0.23.1': optional: true - '@esbuild/openbsd-x64@0.19.12': + '@esbuild/openbsd-arm64@0.23.1': optional: true '@esbuild/openbsd-x64@0.21.5': optional: true - '@esbuild/sunos-x64@0.18.20': - optional: true - - '@esbuild/sunos-x64@0.19.12': + '@esbuild/openbsd-x64@0.23.1': optional: true '@esbuild/sunos-x64@0.21.5': optional: true - '@esbuild/win32-arm64@0.18.20': - optional: true - - '@esbuild/win32-arm64@0.19.12': + '@esbuild/sunos-x64@0.23.1': optional: true '@esbuild/win32-arm64@0.21.5': optional: true - '@esbuild/win32-ia32@0.18.20': - optional: true - - '@esbuild/win32-ia32@0.19.12': + '@esbuild/win32-arm64@0.23.1': optional: true '@esbuild/win32-ia32@0.21.5': optional: true - '@esbuild/win32-x64@0.18.20': - optional: true - - '@esbuild/win32-x64@0.19.12': + '@esbuild/win32-ia32@0.23.1': optional: true '@esbuild/win32-x64@0.21.5': optional: true - '@eslint-community/eslint-utils@4.4.1(eslint@9.17.0(jiti@1.21.7))': + '@esbuild/win32-x64@0.23.1': + optional: true + + '@eslint-community/eslint-utils@4.4.1(eslint@9.17.0(jiti@2.4.2))': dependencies: - eslint: 9.17.0(jiti@1.21.7) + eslint: 9.17.0(jiti@2.4.2) eslint-visitor-keys: 3.4.3 '@eslint-community/regexpp@4.12.1': {} - '@eslint/compat@1.2.4(eslint@9.17.0(jiti@1.21.7))': + '@eslint/compat@1.2.4(eslint@9.17.0(jiti@2.4.2))': optionalDependencies: - eslint: 9.17.0(jiti@1.21.7) + eslint: 9.17.0(jiti@2.4.2) '@eslint/config-array@0.19.1': dependencies: @@ -2843,17 +2649,23 @@ snapshots: dependencies: undici-types: 6.20.0 + '@types/pg@8.11.10': + dependencies: + '@types/node': 22.10.5 + pg-protocol: 1.7.0 + pg-types: 4.0.2 + '@types/resolve@1.20.2': {} - '@typescript-eslint/eslint-plugin@8.19.1(@typescript-eslint/parser@8.19.1(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.3))(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.3)': + '@typescript-eslint/eslint-plugin@8.19.1(@typescript-eslint/parser@8.19.1(eslint@9.17.0(jiti@2.4.2))(typescript@5.7.3))(eslint@9.17.0(jiti@2.4.2))(typescript@5.7.3)': dependencies: '@eslint-community/regexpp': 4.12.1 - '@typescript-eslint/parser': 8.19.1(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.3) + '@typescript-eslint/parser': 8.19.1(eslint@9.17.0(jiti@2.4.2))(typescript@5.7.3) '@typescript-eslint/scope-manager': 8.19.1 - '@typescript-eslint/type-utils': 8.19.1(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.3) - '@typescript-eslint/utils': 8.19.1(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.3) + '@typescript-eslint/type-utils': 8.19.1(eslint@9.17.0(jiti@2.4.2))(typescript@5.7.3) + '@typescript-eslint/utils': 8.19.1(eslint@9.17.0(jiti@2.4.2))(typescript@5.7.3) '@typescript-eslint/visitor-keys': 8.19.1 - eslint: 9.17.0(jiti@1.21.7) + eslint: 9.17.0(jiti@2.4.2) graphemer: 1.4.0 ignore: 5.3.2 natural-compare: 1.4.0 @@ -2862,14 +2674,14 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.19.1(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.3)': + '@typescript-eslint/parser@8.19.1(eslint@9.17.0(jiti@2.4.2))(typescript@5.7.3)': dependencies: '@typescript-eslint/scope-manager': 8.19.1 '@typescript-eslint/types': 8.19.1 '@typescript-eslint/typescript-estree': 8.19.1(typescript@5.7.3) '@typescript-eslint/visitor-keys': 8.19.1 debug: 4.4.0 - eslint: 9.17.0(jiti@1.21.7) + eslint: 9.17.0(jiti@2.4.2) typescript: 5.7.3 transitivePeerDependencies: - supports-color @@ -2879,12 +2691,12 @@ snapshots: '@typescript-eslint/types': 8.19.1 '@typescript-eslint/visitor-keys': 8.19.1 - '@typescript-eslint/type-utils@8.19.1(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.3)': + '@typescript-eslint/type-utils@8.19.1(eslint@9.17.0(jiti@2.4.2))(typescript@5.7.3)': dependencies: '@typescript-eslint/typescript-estree': 8.19.1(typescript@5.7.3) - '@typescript-eslint/utils': 8.19.1(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.3) + '@typescript-eslint/utils': 8.19.1(eslint@9.17.0(jiti@2.4.2))(typescript@5.7.3) debug: 4.4.0 - eslint: 9.17.0(jiti@1.21.7) + eslint: 9.17.0(jiti@2.4.2) ts-api-utils: 2.0.0(typescript@5.7.3) typescript: 5.7.3 transitivePeerDependencies: @@ -2906,13 +2718,13 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.19.1(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.3)': + '@typescript-eslint/utils@8.19.1(eslint@9.17.0(jiti@2.4.2))(typescript@5.7.3)': dependencies: - '@eslint-community/eslint-utils': 4.4.1(eslint@9.17.0(jiti@1.21.7)) + '@eslint-community/eslint-utils': 4.4.1(eslint@9.17.0(jiti@2.4.2)) '@typescript-eslint/scope-manager': 8.19.1 '@typescript-eslint/types': 8.19.1 '@typescript-eslint/typescript-estree': 8.19.1(typescript@5.7.3) - eslint: 9.17.0(jiti@1.21.7) + eslint: 9.17.0(jiti@2.4.2) typescript: 5.7.3 transitivePeerDependencies: - supports-color @@ -2995,25 +2807,8 @@ snapshots: balanced-match@1.0.2: {} - base64-js@1.5.1: {} - - better-sqlite3@11.7.2: - dependencies: - bindings: 1.5.0 - prebuild-install: 7.1.2 - binary-extensions@2.3.0: {} - bindings@1.5.0: - dependencies: - file-uri-to-path: 1.0.0 - - bl@4.1.0: - dependencies: - buffer: 5.7.1 - inherits: 2.0.4 - readable-stream: 3.6.2 - brace-expansion@1.1.11: dependencies: balanced-match: 1.0.2 @@ -3034,12 +2829,20 @@ snapshots: node-releases: 2.0.19 update-browserslist-db: 1.1.2(browserslist@4.24.4) - buffer-from@1.1.2: {} - - buffer@5.7.1: + c12@2.0.1: dependencies: - base64-js: 1.5.1 - ieee754: 1.2.1 + chokidar: 4.0.3 + confbox: 0.1.8 + defu: 6.1.4 + dotenv: 16.4.7 + giget: 1.2.3 + jiti: 2.4.2 + mlly: 1.7.3 + ohash: 1.1.4 + pathe: 1.1.2 + perfect-debounce: 1.0.0 + pkg-types: 1.3.0 + rc9: 2.1.2 callsites@3.1.0: {} @@ -3068,7 +2871,11 @@ snapshots: dependencies: readdirp: 4.0.2 - chownr@1.1.4: {} + chownr@2.0.0: {} + + citty@0.1.6: + dependencies: + consola: 3.4.0 clsx@2.1.1: {} @@ -3090,6 +2897,8 @@ snapshots: confbox@0.1.8: {} + consola@3.4.0: {} + cookie@0.6.0: {} cron-parser@4.9.0: @@ -3108,19 +2917,15 @@ snapshots: dependencies: ms: 2.1.3 - decompress-response@6.0.0: - dependencies: - mimic-response: 3.1.0 - - deep-extend@0.6.0: {} - deep-is@0.1.4: {} deepmerge@4.3.1: {} + defu@6.1.4: {} + delayed-stream@1.0.0: {} - detect-libc@2.0.3: {} + destr@2.0.3: {} devalue@5.1.1: {} @@ -3130,18 +2935,7 @@ snapshots: dlv@1.1.3: {} - drizzle-kit@0.22.8: - dependencies: - '@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.33.0(@types/better-sqlite3@7.6.12)(better-sqlite3@11.7.2): - optionalDependencies: - '@types/better-sqlite3': 7.6.12 - better-sqlite3: 11.7.2 + dotenv@16.4.7: {} eastasianwidth@0.2.0: {} @@ -3151,68 +2945,6 @@ snapshots: emoji-regex@9.2.2: {} - end-of-stream@1.4.4: - dependencies: - once: 1.4.0 - - esbuild-register@3.6.0(esbuild@0.19.12): - dependencies: - debug: 4.4.0 - esbuild: 0.19.12 - transitivePeerDependencies: - - supports-color - - esbuild@0.18.20: - optionalDependencies: - '@esbuild/android-arm': 0.18.20 - '@esbuild/android-arm64': 0.18.20 - '@esbuild/android-x64': 0.18.20 - '@esbuild/darwin-arm64': 0.18.20 - '@esbuild/darwin-x64': 0.18.20 - '@esbuild/freebsd-arm64': 0.18.20 - '@esbuild/freebsd-x64': 0.18.20 - '@esbuild/linux-arm': 0.18.20 - '@esbuild/linux-arm64': 0.18.20 - '@esbuild/linux-ia32': 0.18.20 - '@esbuild/linux-loong64': 0.18.20 - '@esbuild/linux-mips64el': 0.18.20 - '@esbuild/linux-ppc64': 0.18.20 - '@esbuild/linux-riscv64': 0.18.20 - '@esbuild/linux-s390x': 0.18.20 - '@esbuild/linux-x64': 0.18.20 - '@esbuild/netbsd-x64': 0.18.20 - '@esbuild/openbsd-x64': 0.18.20 - '@esbuild/sunos-x64': 0.18.20 - '@esbuild/win32-arm64': 0.18.20 - '@esbuild/win32-ia32': 0.18.20 - '@esbuild/win32-x64': 0.18.20 - - esbuild@0.19.12: - optionalDependencies: - '@esbuild/aix-ppc64': 0.19.12 - '@esbuild/android-arm': 0.19.12 - '@esbuild/android-arm64': 0.19.12 - '@esbuild/android-x64': 0.19.12 - '@esbuild/darwin-arm64': 0.19.12 - '@esbuild/darwin-x64': 0.19.12 - '@esbuild/freebsd-arm64': 0.19.12 - '@esbuild/freebsd-x64': 0.19.12 - '@esbuild/linux-arm': 0.19.12 - '@esbuild/linux-arm64': 0.19.12 - '@esbuild/linux-ia32': 0.19.12 - '@esbuild/linux-loong64': 0.19.12 - '@esbuild/linux-mips64el': 0.19.12 - '@esbuild/linux-ppc64': 0.19.12 - '@esbuild/linux-riscv64': 0.19.12 - '@esbuild/linux-s390x': 0.19.12 - '@esbuild/linux-x64': 0.19.12 - '@esbuild/netbsd-x64': 0.19.12 - '@esbuild/openbsd-x64': 0.19.12 - '@esbuild/sunos-x64': 0.19.12 - '@esbuild/win32-arm64': 0.19.12 - '@esbuild/win32-ia32': 0.19.12 - '@esbuild/win32-x64': 0.19.12 - esbuild@0.21.5: optionalDependencies: '@esbuild/aix-ppc64': 0.21.5 @@ -3239,25 +2971,52 @@ 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 + escalade@3.2.0: {} escape-string-regexp@4.0.0: {} - eslint-compat-utils@0.5.1(eslint@9.17.0(jiti@1.21.7)): + eslint-compat-utils@0.5.1(eslint@9.17.0(jiti@2.4.2)): dependencies: - eslint: 9.17.0(jiti@1.21.7) + eslint: 9.17.0(jiti@2.4.2) semver: 7.6.3 - eslint-config-prettier@9.1.0(eslint@9.17.0(jiti@1.21.7)): + eslint-config-prettier@9.1.0(eslint@9.17.0(jiti@2.4.2)): dependencies: - eslint: 9.17.0(jiti@1.21.7) + eslint: 9.17.0(jiti@2.4.2) - eslint-plugin-svelte@2.46.1(eslint@9.17.0(jiti@1.21.7))(svelte@5.17.1): + eslint-plugin-svelte@2.46.1(eslint@9.17.0(jiti@2.4.2))(svelte@5.17.1): dependencies: - '@eslint-community/eslint-utils': 4.4.1(eslint@9.17.0(jiti@1.21.7)) + '@eslint-community/eslint-utils': 4.4.1(eslint@9.17.0(jiti@2.4.2)) '@jridgewell/sourcemap-codec': 1.5.0 - eslint: 9.17.0(jiti@1.21.7) - eslint-compat-utils: 0.5.1(eslint@9.17.0(jiti@1.21.7)) + eslint: 9.17.0(jiti@2.4.2) + eslint-compat-utils: 0.5.1(eslint@9.17.0(jiti@2.4.2)) esutils: 2.0.3 known-css-properties: 0.35.0 postcss: 8.4.49 @@ -3291,9 +3050,9 @@ snapshots: eslint-visitor-keys@4.2.0: {} - eslint@9.17.0(jiti@1.21.7): + eslint@9.17.0(jiti@2.4.2): dependencies: - '@eslint-community/eslint-utils': 4.4.1(eslint@9.17.0(jiti@1.21.7)) + '@eslint-community/eslint-utils': 4.4.1(eslint@9.17.0(jiti@2.4.2)) '@eslint-community/regexpp': 4.12.1 '@eslint/config-array': 0.19.1 '@eslint/core': 0.9.1 @@ -3328,7 +3087,7 @@ snapshots: natural-compare: 1.4.0 optionator: 0.9.4 optionalDependencies: - jiti: 1.21.7 + jiti: 2.4.2 transitivePeerDependencies: - supports-color @@ -3364,12 +3123,22 @@ snapshots: esutils@2.0.3: {} + execa@8.0.1: + dependencies: + cross-spawn: 7.0.6 + get-stream: 8.0.1 + human-signals: 5.0.0 + is-stream: 3.0.0 + merge-stream: 2.0.0 + npm-run-path: 5.3.0 + onetime: 6.0.0 + signal-exit: 4.1.0 + strip-final-newline: 3.0.0 + exifreader@4.26.0: optionalDependencies: '@xmldom/xmldom': 0.9.6 - expand-template@2.0.3: {} - fast-deep-equal@3.1.3: {} fast-glob@3.3.3: @@ -3398,8 +3167,6 @@ snapshots: file-saver@2.0.5: {} - file-uri-to-path@1.0.0: {} - fill-range@7.1.1: dependencies: to-regex-range: 5.0.1 @@ -3431,18 +3198,31 @@ snapshots: fraction.js@4.3.7: {} - fs-constants@1.0.0: {} + fs-minipass@2.1.0: + dependencies: + minipass: 3.3.6 fsevents@2.3.3: optional: true function-bind@1.1.2: {} + get-stream@8.0.1: {} + get-tsconfig@4.8.1: dependencies: resolve-pkg-maps: 1.0.0 - github-from-package@0.0.0: {} + giget@1.2.3: + dependencies: + citty: 0.1.6 + consola: 3.4.0 + defu: 6.1.4 + node-fetch-native: 1.6.4 + nypm: 0.3.12 + ohash: 1.1.4 + pathe: 1.1.2 + tar: 6.2.1 glob-parent@5.1.2: dependencies: @@ -3479,7 +3259,7 @@ snapshots: heic2any@0.0.4: {} - ieee754@1.2.1: {} + human-signals@5.0.0: {} ignore@5.3.2: {} @@ -3492,10 +3272,6 @@ snapshots: imurmurhash@0.1.4: {} - inherits@2.0.4: {} - - ini@1.3.8: {} - is-binary-path@2.1.0: dependencies: binary-extensions: 2.3.0 @@ -3524,6 +3300,8 @@ snapshots: dependencies: '@types/estree': 1.0.6 + is-stream@3.0.0: {} + isexe@2.0.0: {} jackspeak@3.4.3: @@ -3534,6 +3312,8 @@ snapshots: jiti@1.21.7: {} + jiti@2.4.2: {} + js-yaml@4.1.0: dependencies: argparse: 2.0.1 @@ -3554,6 +3334,23 @@ snapshots: kolorist@1.8.0: {} + kysely-ctl@0.10.1(kysely@0.27.5): + dependencies: + c12: 2.0.1 + citty: 0.1.6 + consola: 3.4.0 + kysely: 0.27.5 + nypm: 0.4.1 + ofetch: 1.4.1 + pathe: 1.1.2 + pkg-types: 1.3.0 + std-env: 3.8.0 + tsx: 4.19.2 + transitivePeerDependencies: + - magicast + + kysely@0.27.5: {} + levn@0.4.1: dependencies: prelude-ls: 1.2.1 @@ -3588,6 +3385,8 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.5.0 + merge-stream@2.0.0: {} + merge2@1.4.1: {} micromatch@4.0.8: @@ -3603,7 +3402,7 @@ snapshots: mime@4.0.6: {} - mimic-response@3.1.0: {} + mimic-fn@4.0.0: {} minimatch@3.1.2: dependencies: @@ -3613,11 +3412,20 @@ snapshots: dependencies: brace-expansion: 2.0.1 - minimist@1.2.8: {} + minipass@3.3.6: + dependencies: + yallist: 4.0.0 + + minipass@5.0.0: {} minipass@7.1.2: {} - mkdirp-classic@0.5.3: {} + minizlib@2.1.2: + dependencies: + minipass: 3.3.6 + yallist: 4.0.0 + + mkdirp@1.0.4: {} mlly@1.7.3: dependencies: @@ -3640,16 +3448,12 @@ snapshots: nanoid@3.3.8: {} - napi-build-utils@1.0.2: {} - natural-compare@1.4.0: {} - node-abi@3.71.0: - dependencies: - semver: 7.6.3 - node-addon-api@8.3.0: {} + node-fetch-native@1.6.4: {} + node-gyp-build@4.8.4: {} node-releases@2.0.19: {} @@ -3664,13 +3468,45 @@ snapshots: normalize-range@0.1.2: {} + npm-run-path@5.3.0: + dependencies: + path-key: 4.0.0 + + nypm@0.3.12: + dependencies: + citty: 0.1.6 + consola: 3.4.0 + execa: 8.0.1 + pathe: 1.1.2 + pkg-types: 1.3.0 + ufo: 1.5.4 + + nypm@0.4.1: + dependencies: + citty: 0.1.6 + consola: 3.4.0 + pathe: 1.1.2 + pkg-types: 1.3.0 + tinyexec: 0.3.2 + ufo: 1.5.4 + object-assign@4.1.1: {} object-hash@3.0.0: {} - once@1.4.0: + obuf@1.1.2: {} + + ofetch@1.4.1: dependencies: - wrappy: 1.0.2 + destr: 2.0.3 + node-fetch-native: 1.6.4 + ufo: 1.5.4 + + ohash@1.1.4: {} + + onetime@6.0.0: + dependencies: + mimic-fn: 4.0.0 optionator@0.9.4: dependencies: @@ -3705,6 +3541,8 @@ snapshots: path-key@3.1.1: {} + path-key@4.0.0: {} + path-parse@1.0.7: {} path-scurry@1.11.1: @@ -3714,6 +3552,55 @@ snapshots: pathe@1.1.2: {} + perfect-debounce@1.0.0: {} + + pg-cloudflare@1.1.1: + optional: true + + pg-connection-string@2.7.0: {} + + pg-int8@1.0.1: {} + + pg-numeric@1.0.2: {} + + pg-pool@3.7.0(pg@8.13.1): + dependencies: + pg: 8.13.1 + + pg-protocol@1.7.0: {} + + pg-types@2.2.0: + dependencies: + pg-int8: 1.0.1 + postgres-array: 2.0.0 + postgres-bytea: 1.0.0 + postgres-date: 1.0.7 + postgres-interval: 1.2.0 + + pg-types@4.0.2: + dependencies: + pg-int8: 1.0.1 + pg-numeric: 1.0.2 + postgres-array: 3.0.2 + postgres-bytea: 3.0.0 + postgres-date: 2.1.0 + postgres-interval: 3.0.0 + postgres-range: 1.1.4 + + pg@8.13.1: + dependencies: + pg-connection-string: 2.7.0 + pg-pool: 3.7.0(pg@8.13.1) + pg-protocol: 1.7.0 + pg-types: 2.2.0 + pgpass: 1.0.5 + optionalDependencies: + pg-cloudflare: 1.1.1 + + pgpass@1.0.5: + dependencies: + split2: 4.2.0 + picocolors@1.1.1: {} picomatch@2.3.1: {} @@ -3782,20 +3669,27 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 - prebuild-install@7.1.2: + postgres-array@2.0.0: {} + + postgres-array@3.0.2: {} + + postgres-bytea@1.0.0: {} + + postgres-bytea@3.0.0: dependencies: - detect-libc: 2.0.3 - expand-template: 2.0.3 - github-from-package: 0.0.0 - minimist: 1.2.8 - mkdirp-classic: 0.5.3 - napi-build-utils: 1.0.2 - node-abi: 3.71.0 - pump: 3.0.2 - rc: 1.2.8 - simple-get: 4.0.1 - tar-fs: 2.1.1 - tunnel-agent: 0.6.0 + obuf: 1.1.2 + + postgres-date@1.0.7: {} + + postgres-date@2.1.0: {} + + postgres-interval@1.2.0: + dependencies: + xtend: 4.0.2 + + postgres-interval@3.0.0: {} + + postgres-range@1.1.4: {} prelude-ls@1.2.1: {} @@ -3814,32 +3708,19 @@ snapshots: proxy-from-env@1.1.0: {} - pump@3.0.2: - dependencies: - end-of-stream: 1.4.4 - once: 1.4.0 - punycode@2.3.1: {} queue-microtask@1.2.3: {} - rc@1.2.8: + rc9@2.1.2: dependencies: - deep-extend: 0.6.0 - ini: 1.3.8 - minimist: 1.2.8 - strip-json-comments: 2.0.1 + defu: 6.1.4 + destr: 2.0.3 read-cache@1.0.0: dependencies: pify: 2.3.0 - readable-stream@3.6.2: - dependencies: - inherits: 2.0.4 - string_decoder: 1.3.0 - util-deprecate: 1.0.2 - readdirp@3.6.0: dependencies: picomatch: 2.3.1 @@ -3891,8 +3772,6 @@ snapshots: dependencies: mri: 1.2.0 - safe-buffer@5.2.1: {} - semver@7.6.3: {} set-cookie-parser@2.7.1: {} @@ -3905,14 +3784,6 @@ snapshots: signal-exit@4.1.0: {} - simple-concat@1.0.1: {} - - simple-get@4.0.1: - dependencies: - decompress-response: 6.0.0 - once: 1.4.0 - simple-concat: 1.0.1 - sirv@3.0.0: dependencies: '@polka/url': 1.0.0-next.28 @@ -3923,12 +3794,9 @@ snapshots: source-map-js@1.2.1: {} - source-map-support@0.5.21: - dependencies: - buffer-from: 1.1.2 - source-map: 0.6.1 + split2@4.2.0: {} - source-map@0.6.1: {} + std-env@3.8.0: {} string-width@4.2.3: dependencies: @@ -3942,10 +3810,6 @@ snapshots: emoji-regex: 9.2.2 strip-ansi: 7.1.0 - string_decoder@1.3.0: - dependencies: - safe-buffer: 5.2.1 - strip-ansi@6.0.1: dependencies: ansi-regex: 5.0.1 @@ -3954,7 +3818,7 @@ snapshots: dependencies: ansi-regex: 6.1.0 - strip-json-comments@2.0.1: {} + strip-final-newline@3.0.0: {} strip-json-comments@3.1.1: {} @@ -4040,20 +3904,14 @@ snapshots: transitivePeerDependencies: - ts-node - tar-fs@2.1.1: + tar@6.2.1: dependencies: - chownr: 1.1.4 - mkdirp-classic: 0.5.3 - pump: 3.0.2 - tar-stream: 2.2.0 - - tar-stream@2.2.0: - dependencies: - bl: 4.1.0 - end-of-stream: 1.4.4 - fs-constants: 1.0.0 - inherits: 2.0.4 - readable-stream: 3.6.2 + chownr: 2.0.0 + fs-minipass: 2.1.0 + minipass: 5.0.0 + minizlib: 2.1.2 + mkdirp: 1.0.4 + yallist: 4.0.0 thenify-all@1.6.0: dependencies: @@ -4082,20 +3940,23 @@ snapshots: ts-interface-checker@0.1.13: {} - tunnel-agent@0.6.0: + tsx@4.19.2: dependencies: - safe-buffer: 5.2.1 + esbuild: 0.23.1 + get-tsconfig: 4.8.1 + optionalDependencies: + fsevents: 2.3.3 type-check@0.4.0: dependencies: prelude-ls: 1.2.1 - typescript-eslint@8.19.1(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.3): + typescript-eslint@8.19.1(eslint@9.17.0(jiti@2.4.2))(typescript@5.7.3): dependencies: - '@typescript-eslint/eslint-plugin': 8.19.1(@typescript-eslint/parser@8.19.1(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.3))(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.3) - '@typescript-eslint/parser': 8.19.1(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.3) - '@typescript-eslint/utils': 8.19.1(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.3) - eslint: 9.17.0(jiti@1.21.7) + '@typescript-eslint/eslint-plugin': 8.19.1(@typescript-eslint/parser@8.19.1(eslint@9.17.0(jiti@2.4.2))(typescript@5.7.3))(eslint@9.17.0(jiti@2.4.2))(typescript@5.7.3) + '@typescript-eslint/parser': 8.19.1(eslint@9.17.0(jiti@2.4.2))(typescript@5.7.3) + '@typescript-eslint/utils': 8.19.1(eslint@9.17.0(jiti@2.4.2))(typescript@5.7.3) + eslint: 9.17.0(jiti@2.4.2) typescript: 5.7.3 transitivePeerDependencies: - supports-color @@ -4172,7 +4033,9 @@ snapshots: string-width: 5.1.2 strip-ansi: 7.1.0 - wrappy@1.0.2: {} + xtend@4.0.2: {} + + yallist@4.0.0: {} yaml@1.10.2: {} diff --git a/src/hooks.server.ts b/src/hooks.server.ts index 9dac88c..6f94a7e 100644 --- a/src/hooks.server.ts +++ b/src/hooks.server.ts @@ -2,15 +2,15 @@ import type { ServerInit } from "@sveltejs/kit"; import { sequence } from "@sveltejs/kit/hooks"; import schedule from "node-schedule"; import { cleanupExpiredUserClientChallenges } from "$lib/server/db/client"; -import { migrateDB } from "$lib/server/db/drizzle"; +import { migrateDB } from "$lib/server/db/kysely"; import { cleanupExpiredSessions, cleanupExpiredSessionUpgradeChallenges, } from "$lib/server/db/session"; import { authenticate, setAgentInfo } from "$lib/server/middlewares"; -export const init: ServerInit = () => { - migrateDB(); +export const init: ServerInit = async () => { + await migrateDB(); schedule.scheduleJob("0 * * * *", () => { cleanupExpiredUserClientChallenges(); diff --git a/src/lib/server/db/client.ts b/src/lib/server/db/client.ts index 37a1054..08f2c98 100644 --- a/src/lib/server/db/client.ts +++ b/src/lib/server/db/client.ts @@ -1,53 +1,97 @@ -import { SqliteError } from "better-sqlite3"; -import { and, or, eq, gt, lte } from "drizzle-orm"; -import db from "./drizzle"; +import pg from "pg"; import { IntegrityError } from "./error"; -import { client, userClient, userClientChallenge } from "./schema"; +import db from "./kysely"; +import type { UserClientState } from "./schema"; + +interface Client { + id: number; + encPubKey: string; + sigPubKey: string; +} + +interface UserClient { + userId: number; + clientId: number; + state: UserClientState; +} + +interface UserClientWithDetails extends UserClient { + encPubKey: string; + sigPubKey: string; +} export const createClient = async (encPubKey: string, sigPubKey: string, userId: number) => { - return await db.transaction( - async (tx) => { - const clients = await tx - .select({ id: client.id }) - .from(client) - .where(or(eq(client.encPubKey, sigPubKey), eq(client.sigPubKey, encPubKey))) - .limit(1); - if (clients.length !== 0) { + return await db + .transaction() + .setIsolationLevel("serializable") + .execute(async (trx) => { + const client = await trx + .selectFrom("client") + .where((eb) => + eb.or([ + eb("encryption_public_key", "=", encPubKey), + eb("encryption_public_key", "=", sigPubKey), + eb("signature_public_key", "=", encPubKey), + eb("signature_public_key", "=", sigPubKey), + ]), + ) + .limit(1) + .executeTakeFirst(); + if (client) { throw new IntegrityError("Public key(s) already registered"); } - const newClients = await tx - .insert(client) - .values({ encPubKey, sigPubKey }) - .returning({ id: client.id }); - const { id: clientId } = newClients[0]!; - await tx.insert(userClient).values({ userId, clientId }); - - return clientId; - }, - { behavior: "exclusive" }, - ); + const { clientId } = await trx + .insertInto("client") + .values({ encryption_public_key: encPubKey, signature_public_key: sigPubKey }) + .returning("id as clientId") + .executeTakeFirstOrThrow(); + await trx + .insertInto("user_client") + .values({ user_id: userId, client_id: clientId }) + .execute(); + return { clientId }; + }); }; export const getClient = async (clientId: number) => { - const clients = await db.select().from(client).where(eq(client.id, clientId)).limit(1); - return clients[0] ?? null; + const client = await db + .selectFrom("client") + .selectAll() + .where("id", "=", clientId) + .limit(1) + .executeTakeFirst(); + return client + ? ({ + id: client.id, + encPubKey: client.encryption_public_key, + sigPubKey: client.signature_public_key, + } satisfies Client) + : null; }; export const getClientByPubKeys = async (encPubKey: string, sigPubKey: string) => { - const clients = await db - .select() - .from(client) - .where(and(eq(client.encPubKey, encPubKey), eq(client.sigPubKey, sigPubKey))) - .limit(1); - return clients[0] ?? null; + const client = await db + .selectFrom("client") + .selectAll() + .where("encryption_public_key", "=", encPubKey) + .where("signature_public_key", "=", sigPubKey) + .limit(1) + .executeTakeFirst(); + return client + ? ({ + id: client.id, + encPubKey: client.encryption_public_key, + sigPubKey: client.signature_public_key, + } satisfies Client) + : null; }; export const createUserClient = async (userId: number, clientId: number) => { try { - await db.insert(userClient).values({ userId, clientId }); + await db.insertInto("user_client").values({ user_id: userId, client_id: clientId }).execute(); } catch (e) { - if (e instanceof SqliteError && e.code === "SQLITE_CONSTRAINT_PRIMARYKEY") { + if (e instanceof pg.DatabaseError && e.code === "23505") { throw new IntegrityError("User client already exists"); } throw e; @@ -55,52 +99,76 @@ export const createUserClient = async (userId: number, clientId: number) => { }; export const getAllUserClients = async (userId: number) => { - return await db.select().from(userClient).where(eq(userClient.userId, userId)); + const userClients = await db + .selectFrom("user_client") + .selectAll() + .where("user_id", "=", userId) + .execute(); + return userClients.map( + ({ user_id, client_id, state }) => + ({ + userId: user_id, + clientId: client_id, + state, + }) satisfies UserClient, + ); }; export const getUserClient = async (userId: number, clientId: number) => { - const userClients = await db - .select() - .from(userClient) - .where(and(eq(userClient.userId, userId), eq(userClient.clientId, clientId))) - .limit(1); - return userClients[0] ?? null; + const userClient = await db + .selectFrom("user_client") + .selectAll() + .where("user_id", "=", userId) + .where("client_id", "=", clientId) + .limit(1) + .executeTakeFirst(); + return userClient + ? ({ + userId: userClient.user_id, + clientId: userClient.client_id, + state: userClient.state, + } satisfies UserClient) + : null; }; export const getUserClientWithDetails = async (userId: number, clientId: number) => { - const userClients = await db - .select() - .from(userClient) - .innerJoin(client, eq(userClient.clientId, client.id)) - .where(and(eq(userClient.userId, userId), eq(userClient.clientId, clientId))) - .limit(1); - return userClients[0] ?? null; + const userClient = await db + .selectFrom("user_client") + .innerJoin("client", "user_client.client_id", "client.id") + .selectAll() + .where("user_id", "=", userId) + .where("client_id", "=", clientId) + .limit(1) + .executeTakeFirst(); + return userClient + ? ({ + userId: userClient.user_id, + clientId: userClient.client_id, + state: userClient.state, + encPubKey: userClient.encryption_public_key, + sigPubKey: userClient.signature_public_key, + } satisfies UserClientWithDetails) + : null; }; export const setUserClientStateToPending = async (userId: number, clientId: number) => { await db - .update(userClient) + .updateTable("user_client") .set({ state: "pending" }) - .where( - and( - eq(userClient.userId, userId), - eq(userClient.clientId, clientId), - eq(userClient.state, "challenging"), - ), - ); + .where("user_id", "=", userId) + .where("client_id", "=", clientId) + .where("state", "=", "challenging") + .execute(); }; export const setUserClientStateToActive = async (userId: number, clientId: number) => { await db - .update(userClient) + .updateTable("user_client") .set({ state: "active" }) - .where( - and( - eq(userClient.userId, userId), - eq(userClient.clientId, clientId), - eq(userClient.state, "pending"), - ), - ); + .where("user_id", "=", userId) + .where("client_id", "=", clientId) + .where("state", "=", "pending") + .execute(); }; export const registerUserClientChallenge = async ( @@ -110,30 +178,30 @@ export const registerUserClientChallenge = async ( allowedIp: string, expiresAt: Date, ) => { - await db.insert(userClientChallenge).values({ - userId, - clientId, - answer, - allowedIp, - expiresAt, - }); + await db + .insertInto("user_client_challenge") + .values({ + user_id: userId, + client_id: clientId, + answer, + allowed_ip: allowedIp, + expires_at: expiresAt, + }) + .execute(); }; export const consumeUserClientChallenge = async (userId: number, answer: string, ip: string) => { - const challenges = await db - .delete(userClientChallenge) - .where( - and( - eq(userClientChallenge.userId, userId), - eq(userClientChallenge.answer, answer), - eq(userClientChallenge.allowedIp, ip), - gt(userClientChallenge.expiresAt, new Date()), - ), - ) - .returning({ clientId: userClientChallenge.clientId }); - return challenges[0] ?? null; + const challenge = await db + .deleteFrom("user_client_challenge") + .where("user_id", "=", userId) + .where("answer", "=", answer) + .where("allowed_ip", "=", ip) + .where("expires_at", ">", new Date()) + .returning("client_id") + .executeTakeFirst(); + return challenge ? { clientId: challenge.client_id } : null; }; export const cleanupExpiredUserClientChallenges = async () => { - await db.delete(userClientChallenge).where(lte(userClientChallenge.expiresAt, new Date())); + await db.deleteFrom("user_client_challenge").where("expires_at", "<=", new Date()).execute(); }; diff --git a/src/lib/server/db/drizzle.ts b/src/lib/server/db/drizzle.ts deleted file mode 100644 index 589c91e..0000000 --- a/src/lib/server/db/drizzle.ts +++ /dev/null @@ -1,15 +0,0 @@ -import Database from "better-sqlite3"; -import { drizzle } from "drizzle-orm/better-sqlite3"; -import { migrate } from "drizzle-orm/better-sqlite3/migrator"; -import env from "$lib/server/loadenv"; - -const client = new Database(env.databaseUrl); -const db = drizzle(client); - -export const migrateDB = () => { - if (process.env.NODE_ENV === "production") { - migrate(db, { migrationsFolder: "./drizzle" }); - } -}; - -export default db; diff --git a/src/lib/server/db/file.ts b/src/lib/server/db/file.ts index ff3b0c7..b372557 100644 --- a/src/lib/server/db/file.ts +++ b/src/lib/server/db/file.ts @@ -1,22 +1,23 @@ -import { SqliteError } from "better-sqlite3"; -import { and, eq, isNull } from "drizzle-orm"; -import db from "./drizzle"; import { IntegrityError } from "./error"; -import { directory, directoryLog, file, fileLog, fileCategory, hsk, mek } from "./schema"; +import db from "./kysely"; +import type { Ciphertext } from "./schema"; type DirectoryId = "root" | number; -export interface NewDirectoryParams { +interface Directory { + id: number; parentId: DirectoryId; userId: number; mekVersion: number; encDek: string; dekVersion: Date; - encName: string; - encNameIv: string; + encName: Ciphertext; } -export interface NewFileParams { +export type NewDirectory = Omit; + +interface File { + id: number; parentId: DirectoryId; userId: number; path: string; @@ -28,217 +29,264 @@ export interface NewFileParams { contentType: string; encContentIv: string; encContentHash: string; - encName: string; - encNameIv: string; - encCreatedAt: string | null; - encCreatedAtIv: string | null; - encLastModifiedAt: string; - encLastModifiedAtIv: string; + encName: Ciphertext; + encCreatedAt: Ciphertext | null; + encLastModifiedAt: Ciphertext; } -export const registerDirectory = async (params: NewDirectoryParams) => { - await db.transaction( - async (tx) => { - const meks = await tx - .select({ version: mek.version }) - .from(mek) - .where(and(eq(mek.userId, params.userId), eq(mek.state, "active"))) - .limit(1); - if (meks[0]?.version !== params.mekVersion) { - throw new IntegrityError("Inactive MEK version"); - } +export type NewFile = Omit; - const newDirectories = await tx - .insert(directory) - .values({ - parentId: params.parentId === "root" ? null : params.parentId, - userId: params.userId, - mekVersion: params.mekVersion, - encDek: params.encDek, - dekVersion: params.dekVersion, - encName: { ciphertext: params.encName, iv: params.encNameIv }, - }) - .returning({ id: directory.id }); - const { id: directoryId } = newDirectories[0]!; - await tx.insert(directoryLog).values({ - directoryId, +export const registerDirectory = async (params: NewDirectory) => { + await db.transaction().execute(async (trx) => { + const mek = await trx + .selectFrom("master_encryption_key") + .select("version") + .where("user_id", "=", params.userId) + .where("state", "=", "active") + .limit(1) + .forUpdate() + .executeTakeFirst(); + if (mek?.version !== params.mekVersion) { + throw new IntegrityError("Inactive MEK version"); + } + + const { directoryId } = await trx + .insertInto("directory") + .values({ + parent_id: params.parentId !== "root" ? params.parentId : null, + user_id: params.userId, + master_encryption_key_version: params.mekVersion, + encrypted_data_encryption_key: params.encDek, + data_encryption_key_version: params.dekVersion, + encrypted_name: params.encName, + }) + .returning("id as directoryId") + .executeTakeFirstOrThrow(); + await trx + .insertInto("directory_log") + .values({ + directory_id: directoryId, timestamp: new Date(), action: "create", - newName: { ciphertext: params.encName, iv: params.encNameIv }, - }); - }, - { behavior: "exclusive" }, - ); + new_name: params.encName, + }) + .execute(); + }); }; export const getAllDirectoriesByParent = async (userId: number, parentId: DirectoryId) => { - return await db - .select() - .from(directory) - .where( - and( - eq(directory.userId, userId), - parentId === "root" ? isNull(directory.parentId) : eq(directory.parentId, parentId), - ), - ); + let query = db.selectFrom("directory").selectAll().where("user_id", "=", userId); + query = + parentId === "root" + ? query.where("parent_id", "is", null) + : query.where("parent_id", "=", parentId); + const directories = await query.execute(); + return directories.map( + (directory) => + ({ + id: directory.id, + parentId: directory.parent_id ?? "root", + userId: directory.user_id, + mekVersion: directory.master_encryption_key_version, + encDek: directory.encrypted_data_encryption_key, + dekVersion: directory.data_encryption_key_version, + encName: directory.encrypted_name, + }) satisfies Directory, + ); }; export const getDirectory = async (userId: number, directoryId: number) => { - const res = await db - .select() - .from(directory) - .where(and(eq(directory.userId, userId), eq(directory.id, directoryId))) - .limit(1); - return res[0] ?? null; + const directory = await db + .selectFrom("directory") + .selectAll() + .where("id", "=", directoryId) + .where("user_id", "=", userId) + .limit(1) + .executeTakeFirst(); + return directory + ? ({ + id: directory.id, + parentId: directory.parent_id ?? "root", + userId: directory.user_id, + mekVersion: directory.master_encryption_key_version, + encDek: directory.encrypted_data_encryption_key, + dekVersion: directory.data_encryption_key_version, + encName: directory.encrypted_name, + } satisfies Directory) + : null; }; export const setDirectoryEncName = async ( userId: number, directoryId: number, dekVersion: Date, - encName: string, - encNameIv: string, + encName: Ciphertext, ) => { - await db.transaction( - async (tx) => { - const directories = await tx - .select({ version: directory.dekVersion }) - .from(directory) - .where(and(eq(directory.userId, userId), eq(directory.id, directoryId))) - .limit(1); - if (!directories[0]) { - throw new IntegrityError("Directory not found"); - } else if (directories[0].version.getTime() !== dekVersion.getTime()) { - throw new IntegrityError("Invalid DEK version"); - } + await db.transaction().execute(async (trx) => { + const directory = await trx + .selectFrom("directory") + .select("data_encryption_key_version") + .where("id", "=", directoryId) + .where("user_id", "=", userId) + .limit(1) + .forUpdate() + .executeTakeFirst(); + if (!directory) { + throw new IntegrityError("Directory not found"); + } else if (directory.data_encryption_key_version.getTime() !== dekVersion.getTime()) { + throw new IntegrityError("Invalid DEK version"); + } - await tx - .update(directory) - .set({ encName: { ciphertext: encName, iv: encNameIv } }) - .where(and(eq(directory.userId, userId), eq(directory.id, directoryId))); - await tx.insert(directoryLog).values({ - directoryId, + await trx + .updateTable("directory") + .set({ encrypted_name: encName }) + .where("id", "=", directoryId) + .where("user_id", "=", userId) + .execute(); + await trx + .insertInto("directory_log") + .values({ + directory_id: directoryId, timestamp: new Date(), action: "rename", - newName: { ciphertext: encName, iv: encNameIv }, - }); - }, - { behavior: "exclusive" }, - ); + new_name: encName, + }) + .execute(); + }); }; export const unregisterDirectory = async (userId: number, directoryId: number) => { - return await db.transaction( - async (tx) => { + return await db + .transaction() + .setIsolationLevel("repeatable read") // TODO: Sufficient? + .execute(async (trx) => { const unregisterFiles = async (parentId: number) => { - return await tx - .delete(file) - .where(and(eq(file.userId, userId), eq(file.parentId, parentId))) - .returning({ id: file.id, path: file.path }); + return await trx + .deleteFrom("file") + .where("parent_id", "=", parentId) + .where("user_id", "=", userId) + .returning(["id", "path"]) + .execute(); }; const unregisterDirectoryRecursively = async ( directoryId: number, ): Promise<{ id: number; path: string }[]> => { const files = await unregisterFiles(directoryId); - const subDirectories = await tx - .select({ id: directory.id }) - .from(directory) - .where(and(eq(directory.userId, userId), eq(directory.parentId, directoryId))); + const subDirectories = await trx + .selectFrom("directory") + .select("id") + .where("parent_id", "=", directoryId) + .where("user_id", "=", userId) + .execute(); const subDirectoryFilePaths = await Promise.all( subDirectories.map(async ({ id }) => await unregisterDirectoryRecursively(id)), ); - const deleteRes = await tx.delete(directory).where(eq(directory.id, directoryId)); - if (deleteRes.changes === 0) { + const deleteRes = await trx + .deleteFrom("directory") + .where("id", "=", directoryId) + .where("user_id", "=", userId) + .executeTakeFirst(); + if (deleteRes.numDeletedRows === 0n) { throw new IntegrityError("Directory not found"); } return files.concat(...subDirectoryFilePaths); }; return await unregisterDirectoryRecursively(directoryId); - }, - { behavior: "exclusive" }, - ); + }); }; -export const registerFile = async (params: NewFileParams) => { - if ( - (params.hskVersion && !params.contentHmac) || - (!params.hskVersion && params.contentHmac) || - (params.encCreatedAt && !params.encCreatedAtIv) || - (!params.encCreatedAt && params.encCreatedAtIv) - ) { +export const registerFile = async (params: NewFile) => { + if ((params.hskVersion && !params.contentHmac) || (!params.hskVersion && params.contentHmac)) { throw new Error("Invalid arguments"); } - await db.transaction( - async (tx) => { - const meks = await tx - .select({ version: mek.version }) - .from(mek) - .where(and(eq(mek.userId, params.userId), eq(mek.state, "active"))) - .limit(1); - if (meks[0]?.version !== params.mekVersion) { - throw new IntegrityError("Inactive MEK version"); - } + await db.transaction().execute(async (trx) => { + const mek = await trx + .selectFrom("master_encryption_key") + .select("version") + .where("user_id", "=", params.userId) + .where("state", "=", "active") + .limit(1) + .forUpdate() + .executeTakeFirst(); + if (mek?.version !== params.mekVersion) { + throw new IntegrityError("Inactive MEK version"); + } - if (params.hskVersion) { - const hsks = await tx - .select({ version: hsk.version }) - .from(hsk) - .where(and(eq(hsk.userId, params.userId), eq(hsk.state, "active"))) - .limit(1); - if (hsks[0]?.version !== params.hskVersion) { - throw new IntegrityError("Inactive HSK version"); - } + if (params.hskVersion) { + const hsk = await trx + .selectFrom("hmac_secret_key") + .select("version") + .where("user_id", "=", params.userId) + .where("state", "=", "active") + .limit(1) + .forUpdate() + .executeTakeFirst(); + if (hsk?.version !== params.hskVersion) { + throw new IntegrityError("Inactive HSK version"); } + } - const newFiles = await tx - .insert(file) - .values({ - path: params.path, - parentId: params.parentId === "root" ? null : params.parentId, - userId: params.userId, - mekVersion: params.mekVersion, - hskVersion: params.hskVersion, - encDek: params.encDek, - dekVersion: params.dekVersion, - contentHmac: params.contentHmac, - contentType: params.contentType, - encContentIv: params.encContentIv, - encContentHash: params.encContentHash, - encName: { ciphertext: params.encName, iv: params.encNameIv }, - encCreatedAt: - params.encCreatedAt && params.encCreatedAtIv - ? { ciphertext: params.encCreatedAt, iv: params.encCreatedAtIv } - : null, - encLastModifiedAt: { - ciphertext: params.encLastModifiedAt, - iv: params.encLastModifiedAtIv, - }, - }) - .returning({ id: file.id }); - const { id: fileId } = newFiles[0]!; - await tx.insert(fileLog).values({ - fileId, + const { fileId } = await trx + .insertInto("file") + .values({ + parent_id: params.parentId !== "root" ? params.parentId : null, + user_id: params.userId, + path: params.path, + master_encryption_key_version: params.mekVersion, + encrypted_data_encryption_key: params.encDek, + data_encryption_key_version: params.dekVersion, + hmac_secret_key_version: params.hskVersion, + content_hmac: params.contentHmac, + content_type: params.contentType, + encrypted_content_iv: params.encContentIv, + encrypted_content_hash: params.encContentHash, + encrypted_name: params.encName, + encrypted_created_at: params.encCreatedAt, + encrypted_last_modified_at: params.encLastModifiedAt, + }) + .returning("id as fileId") + .executeTakeFirstOrThrow(); + await trx + .insertInto("file_log") + .values({ + file_id: fileId, timestamp: new Date(), action: "create", - newName: { ciphertext: params.encName, iv: params.encNameIv }, - }); - }, - { behavior: "exclusive" }, - ); + new_name: params.encName, + }) + .execute(); + }); }; export const getAllFilesByParent = async (userId: number, parentId: DirectoryId) => { - return await db - .select() - .from(file) - .where( - and( - eq(file.userId, userId), - parentId === "root" ? isNull(file.parentId) : eq(file.parentId, parentId), - ), - ); + let query = db.selectFrom("file").selectAll().where("user_id", "=", userId); + query = + parentId === "root" + ? query.where("parent_id", "is", null) + : query.where("parent_id", "=", parentId); + const files = await query.execute(); + return files.map( + (file) => + ({ + id: file.id, + parentId: file.parent_id ?? "root", + userId: file.user_id, + path: file.path, + mekVersion: file.master_encryption_key_version, + encDek: file.encrypted_data_encryption_key, + dekVersion: file.data_encryption_key_version, + hskVersion: file.hmac_secret_key_version, + contentHmac: file.content_hmac, + contentType: file.content_type, + encContentIv: file.encrypted_content_iv, + encContentHash: file.encrypted_content_hash, + encName: file.encrypted_name, + encCreatedAt: file.encrypted_created_at, + encLastModifiedAt: file.encrypted_last_modified_at, + }) satisfies File, + ); }; export const getAllFilesByCategory = async (userId: number, categoryId: number) => { @@ -254,71 +302,95 @@ export const getAllFileIdsByContentHmac = async ( hskVersion: number, contentHmac: string, ) => { - return await db - .select({ id: file.id }) - .from(file) - .where( - and( - eq(file.userId, userId), - eq(file.hskVersion, hskVersion), - eq(file.contentHmac, contentHmac), - ), - ); + const files = await db + .selectFrom("file") + .select("id") + .where("user_id", "=", userId) + .where("hmac_secret_key_version", "=", hskVersion) + .where("content_hmac", "=", contentHmac) + .execute(); + return files.map(({ id }) => ({ id })); }; export const getFile = async (userId: number, fileId: number) => { - const res = await db - .select() - .from(file) - .where(and(eq(file.userId, userId), eq(file.id, fileId))) - .limit(1); - return res[0] ?? null; + const file = await db + .selectFrom("file") + .selectAll() + .where("id", "=", fileId) + .where("user_id", "=", userId) + .limit(1) + .executeTakeFirst(); + return file + ? ({ + id: file.id, + parentId: file.parent_id ?? "root", + userId: file.user_id, + path: file.path, + mekVersion: file.master_encryption_key_version, + encDek: file.encrypted_data_encryption_key, + dekVersion: file.data_encryption_key_version, + hskVersion: file.hmac_secret_key_version, + contentHmac: file.content_hmac, + contentType: file.content_type, + encContentIv: file.encrypted_content_iv, + encContentHash: file.encrypted_content_hash, + encName: file.encrypted_name, + encCreatedAt: file.encrypted_created_at, + encLastModifiedAt: file.encrypted_last_modified_at, + } satisfies File) + : null; }; export const setFileEncName = async ( userId: number, fileId: number, dekVersion: Date, - encName: string, - encNameIv: string, + encName: Ciphertext, ) => { - await db.transaction( - async (tx) => { - const files = await tx - .select({ version: file.dekVersion }) - .from(file) - .where(and(eq(file.userId, userId), eq(file.id, fileId))) - .limit(1); - if (!files[0]) { - throw new IntegrityError("File not found"); - } else if (files[0].version.getTime() !== dekVersion.getTime()) { - throw new IntegrityError("Invalid DEK version"); - } + await db.transaction().execute(async (trx) => { + const file = await trx + .selectFrom("file") + .select("data_encryption_key_version") + .where("id", "=", fileId) + .where("user_id", "=", userId) + .limit(1) + .forUpdate() + .executeTakeFirst(); + if (!file) { + throw new IntegrityError("File not found"); + } else if (file.data_encryption_key_version.getTime() !== dekVersion.getTime()) { + throw new IntegrityError("Invalid DEK version"); + } - await tx - .update(file) - .set({ encName: { ciphertext: encName, iv: encNameIv } }) - .where(and(eq(file.userId, userId), eq(file.id, fileId))); - await tx.insert(fileLog).values({ - fileId, + await trx + .updateTable("file") + .set({ encrypted_name: encName }) + .where("id", "=", fileId) + .where("user_id", "=", userId) + .execute(); + await trx + .insertInto("file_log") + .values({ + file_id: fileId, timestamp: new Date(), action: "rename", - newName: { ciphertext: encName, iv: encNameIv }, - }); - }, - { behavior: "exclusive" }, - ); + new_name: encName, + }) + .execute(); + }); }; export const unregisterFile = async (userId: number, fileId: number) => { - const files = await db - .delete(file) - .where(and(eq(file.userId, userId), eq(file.id, fileId))) - .returning({ path: file.path }); - if (!files[0]) { + const file = await db + .deleteFrom("file") + .where("id", "=", fileId) + .where("user_id", "=", userId) + .returning("path") + .executeTakeFirst(); + if (!file) { throw new IntegrityError("File not found"); } - return files[0].path; + return { path: file.path }; }; export const addFileToCategory = async (fileId: number, categoryId: number) => { diff --git a/src/lib/server/db/hsk.ts b/src/lib/server/db/hsk.ts index faf7dc6..4673cae 100644 --- a/src/lib/server/db/hsk.ts +++ b/src/lib/server/db/hsk.ts @@ -1,8 +1,15 @@ -import { SqliteError } from "better-sqlite3"; -import { and, eq } from "drizzle-orm"; -import db from "./drizzle"; +import pg from "pg"; import { IntegrityError } from "./error"; -import { hsk, hskLog } from "./schema"; +import db from "./kysely"; +import type { HskState } from "./schema"; + +interface Hsk { + userId: number; + version: number; + state: HskState; + mekVersion: number; + encHsk: string; +} export const registerInitialHsk = async ( userId: number, @@ -10,37 +17,52 @@ export const registerInitialHsk = async ( mekVersion: number, encHsk: string, ) => { - await db.transaction( - async (tx) => { - try { - await tx.insert(hsk).values({ - userId, + await db.transaction().execute(async (trx) => { + try { + await trx + .insertInto("hmac_secret_key") + .values({ + user_id: userId, version: 1, state: "active", - mekVersion, - encHsk, - }); - await tx.insert(hskLog).values({ - userId, - hskVersion: 1, + master_encryption_key_version: mekVersion, + encrypted_key: encHsk, + }) + .execute(); + await trx + .insertInto("hmac_secret_key_log") + .values({ + user_id: userId, + hmac_secret_key_version: 1, timestamp: new Date(), action: "create", - actionBy: createdBy, - }); - } catch (e) { - if (e instanceof SqliteError && e.code === "SQLITE_CONSTRAINT_PRIMARYKEY") { - throw new IntegrityError("HSK already registered"); - } - throw e; + action_by: createdBy, + }) + .execute(); + } catch (e) { + if (e instanceof pg.DatabaseError && e.code === "23505") { + throw new IntegrityError("HSK already registered"); } - }, - { behavior: "exclusive" }, - ); + throw e; + } + }); }; export const getAllValidHsks = async (userId: number) => { - return await db - .select() - .from(hsk) - .where(and(eq(hsk.userId, userId), eq(hsk.state, "active"))); + const hsks = await db + .selectFrom("hmac_secret_key") + .selectAll() + .where("user_id", "=", userId) + .where("state", "=", "active") + .execute(); + return hsks.map( + ({ user_id, version, state, master_encryption_key_version, encrypted_key }) => + ({ + userId: user_id, + version, + state: state as "active", + mekVersion: master_encryption_key_version, + encHsk: encrypted_key, + }) satisfies Hsk, + ); }; diff --git a/src/lib/server/db/kysely.ts b/src/lib/server/db/kysely.ts new file mode 100644 index 0000000..302049e --- /dev/null +++ b/src/lib/server/db/kysely.ts @@ -0,0 +1,47 @@ +import { Kysely, PostgresDialect, Migrator } from "kysely"; +import pg from "pg"; +import env from "$lib/server/loadenv"; +import migrations from "./migrations"; +import type { Database } from "./schema"; + +const dialect = new PostgresDialect({ + pool: new pg.Pool({ + host: env.database.host, + port: env.database.port, + user: env.database.user, + password: env.database.password, + database: env.database.name, + }), +}); + +const db = new Kysely({ dialect }); + +export const migrateDB = async () => { + if (env.nodeEnv !== "production") return; + + const migrator = new Migrator({ + db, + provider: { + async getMigrations() { + return migrations; + }, + }, + }); + const { error, results } = await migrator.migrateToLatest(); + if (error) { + const migration = results?.find(({ status }) => status === "Error"); + if (migration) { + console.error(`Migration "${migration.migrationName}" failed.`); + } + console.error(error); + process.exit(1); + } + + if (results?.length === 0) { + console.log("Database is up-to-date."); + } else { + console.log("Database migration completed."); + } +}; + +export default db; diff --git a/src/lib/server/db/mek.ts b/src/lib/server/db/mek.ts index 944636e..d6eecb0 100644 --- a/src/lib/server/db/mek.ts +++ b/src/lib/server/db/mek.ts @@ -1,8 +1,19 @@ -import { SqliteError } from "better-sqlite3"; -import { and, or, eq } from "drizzle-orm"; -import db from "./drizzle"; +import pg from "pg"; import { IntegrityError } from "./error"; -import { mek, mekLog, clientMek } from "./schema"; +import db from "./kysely"; +import type { MekState } from "./schema"; + +interface Mek { + userId: number; + version: number; + state: MekState; +} + +interface ClientMekWithDetails extends Mek { + clientId: number; + encMek: string; + encMekSig: string; +} export const registerInitialMek = async ( userId: number, @@ -10,58 +21,80 @@ export const registerInitialMek = async ( encMek: string, encMekSig: string, ) => { - await db.transaction( - async (tx) => { - try { - await tx.insert(mek).values({ - userId, + await db.transaction().execute(async (trx) => { + try { + await trx + .insertInto("master_encryption_key") + .values({ + user_id: userId, version: 1, state: "active", - }); - await tx.insert(clientMek).values({ - userId, - clientId: createdBy, - mekVersion: 1, - encMek, - encMekSig, - }); - await tx.insert(mekLog).values({ - userId, - mekVersion: 1, + }) + .execute(); + await trx + .insertInto("client_master_encryption_key") + .values({ + user_id: userId, + client_id: createdBy, + version: 1, + encrypted_key: encMek, + encrypted_key_signature: encMekSig, + }) + .execute(); + await trx + .insertInto("master_encryption_key_log") + .values({ + user_id: userId, + master_encryption_key_version: 1, timestamp: new Date(), action: "create", - actionBy: createdBy, - }); - } catch (e) { - if (e instanceof SqliteError && e.code === "SQLITE_CONSTRAINT_PRIMARYKEY") { - throw new IntegrityError("MEK already registered"); - } - throw e; + action_by: createdBy, + }) + .execute(); + } catch (e) { + if (e instanceof pg.DatabaseError && e.code === "23505") { + throw new IntegrityError("MEK already registered"); } - }, - { behavior: "exclusive" }, - ); + throw e; + } + }); }; export const getInitialMek = async (userId: number) => { - const meks = await db - .select() - .from(mek) - .where(and(eq(mek.userId, userId), eq(mek.version, 1))) - .limit(1); - return meks[0] ?? null; + const mek = await db + .selectFrom("master_encryption_key") + .selectAll() + .where("user_id", "=", userId) + .where("version", "=", 1) + .limit(1) + .executeTakeFirst(); + return mek + ? ({ userId: mek.user_id, version: mek.version, state: mek.state } satisfies Mek) + : null; }; export const getAllValidClientMeks = async (userId: number, clientId: number) => { - return await db - .select() - .from(clientMek) - .innerJoin(mek, and(eq(clientMek.userId, mek.userId), eq(clientMek.mekVersion, mek.version))) - .where( - and( - eq(clientMek.userId, userId), - eq(clientMek.clientId, clientId), - or(eq(mek.state, "active"), eq(mek.state, "retired")), - ), - ); + const clientMeks = await db + .selectFrom("client_master_encryption_key") + .innerJoin("master_encryption_key", (join) => + join + .onRef("client_master_encryption_key.user_id", "=", "master_encryption_key.user_id") + .onRef("client_master_encryption_key.version", "=", "master_encryption_key.version"), + ) + .selectAll() + .where("client_master_encryption_key.user_id", "=", userId) + .where("client_master_encryption_key.client_id", "=", clientId) + .where((eb) => eb.or([eb("state", "=", "active"), eb("state", "=", "retired")])) + .execute(); + return clientMeks.map( + ({ user_id, client_id, version, state, encrypted_key, encrypted_key_signature }) => + ({ + userId: user_id, + version, + state: state as "active" | "retired", + clientId: client_id, + encMek: encrypted_key, + encMekSig: encrypted_key_signature, + }) satisfies ClientMekWithDetails, + ); }; diff --git a/src/lib/server/db/migrations/1737357000-Initial.ts b/src/lib/server/db/migrations/1737357000-Initial.ts new file mode 100644 index 0000000..5caf503 --- /dev/null +++ b/src/lib/server/db/migrations/1737357000-Initial.ts @@ -0,0 +1,224 @@ +import { Kysely } from "kysely"; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export const up = async (db: Kysely) => { + // user.ts + await db.schema + .createTable("user") + .addColumn("id", "integer", (col) => col.primaryKey().generatedAlwaysAsIdentity()) + .addColumn("email", "text", (col) => col.unique().notNull()) + .addColumn("nickname", "text", (col) => col.notNull()) + .addColumn("password", "text", (col) => col.notNull()) + .execute(); + + // client.ts + await db.schema + .createTable("client") + .addColumn("id", "integer", (col) => col.primaryKey().generatedAlwaysAsIdentity()) + .addColumn("encryption_public_key", "text", (col) => col.unique().notNull()) + .addColumn("signature_public_key", "text", (col) => col.unique().notNull()) + .addUniqueConstraint("client_ak01", ["encryption_public_key", "signature_public_key"]) + .execute(); + await db.schema + .createTable("user_client") + .addColumn("user_id", "integer", (col) => col.references("user.id").notNull()) + .addColumn("client_id", "integer", (col) => col.references("client.id").notNull()) + .addColumn("state", "text", (col) => col.notNull().defaultTo("challenging")) + .addPrimaryKeyConstraint("user_client_pk", ["user_id", "client_id"]) + .execute(); + await db.schema + .createTable("user_client_challenge") + .addColumn("id", "integer", (col) => col.primaryKey().generatedAlwaysAsIdentity()) + .addColumn("user_id", "integer", (col) => col.references("user.id").notNull()) + .addColumn("client_id", "integer", (col) => col.references("client.id").notNull()) + .addColumn("answer", "text", (col) => col.unique().notNull()) + .addColumn("allowed_ip", "text", (col) => col.notNull()) + .addColumn("expires_at", "timestamp(3)", (col) => col.notNull()) + .addForeignKeyConstraint( + "user_client_challenge_fk01", + ["user_id", "client_id"], + "user_client", + ["user_id", "client_id"], + ) + .execute(); + + // session.ts + await db.schema + .createTable("session") + .addColumn("id", "text", (col) => col.primaryKey()) + .addColumn("user_id", "integer", (col) => col.references("user.id").notNull()) + .addColumn("client_id", "integer", (col) => col.references("client.id")) + .addColumn("created_at", "timestamp(3)", (col) => col.notNull()) + .addColumn("last_used_at", "timestamp(3)", (col) => col.notNull()) + .addColumn("last_used_by_ip", "text") + .addColumn("last_used_by_agent", "text") + .addUniqueConstraint("session_ak01", ["user_id", "client_id"]) + .execute(); + await db.schema + .createTable("session_upgrade_challenge") + .addColumn("id", "integer", (col) => col.primaryKey().generatedAlwaysAsIdentity()) + .addColumn("session_id", "text", (col) => col.references("session.id").unique().notNull()) + .addColumn("client_id", "integer", (col) => col.references("client.id").notNull()) + .addColumn("answer", "text", (col) => col.unique().notNull()) + .addColumn("allowed_ip", "text", (col) => col.notNull()) + .addColumn("expires_at", "timestamp(3)", (col) => col.notNull()) + .execute(); + + // mek.ts + await db.schema + .createTable("master_encryption_key") + .addColumn("user_id", "integer", (col) => col.references("user.id").notNull()) + .addColumn("version", "integer", (col) => col.notNull()) + .addColumn("state", "text", (col) => col.notNull()) + .addPrimaryKeyConstraint("master_encryption_key_pk", ["user_id", "version"]) + .execute(); + await db.schema + .createTable("master_encryption_key_log") + .addColumn("id", "integer", (col) => col.primaryKey().generatedAlwaysAsIdentity()) + .addColumn("user_id", "integer", (col) => col.references("user.id").notNull()) + .addColumn("master_encryption_key_version", "integer", (col) => col.notNull()) + .addColumn("timestamp", "timestamp(3)", (col) => col.notNull()) + .addColumn("action", "text", (col) => col.notNull()) + .addColumn("action_by", "integer", (col) => col.references("client.id")) + .addForeignKeyConstraint( + "master_encryption_key_log_fk01", + ["user_id", "master_encryption_key_version"], + "master_encryption_key", + ["user_id", "version"], + ) + .execute(); + await db.schema + .createTable("client_master_encryption_key") + .addColumn("user_id", "integer", (col) => col.references("user.id").notNull()) + .addColumn("client_id", "integer", (col) => col.references("client.id").notNull()) + .addColumn("version", "integer", (col) => col.notNull()) + .addColumn("encrypted_key", "text", (col) => col.notNull()) + .addColumn("encrypted_key_signature", "text", (col) => col.notNull()) + .addPrimaryKeyConstraint("client_master_encryption_key_pk", ["user_id", "client_id", "version"]) + .addForeignKeyConstraint( + "client_master_encryption_key_fk01", + ["user_id", "version"], + "master_encryption_key", + ["user_id", "version"], + ) + .execute(); + + // hsk.ts + await db.schema + .createTable("hmac_secret_key") + .addColumn("user_id", "integer", (col) => col.references("user.id").notNull()) + .addColumn("version", "integer", (col) => col.notNull()) + .addColumn("state", "text", (col) => col.notNull()) + .addColumn("master_encryption_key_version", "integer", (col) => col.notNull()) + .addColumn("encrypted_key", "text", (col) => col.unique().notNull()) + .addPrimaryKeyConstraint("hmac_secret_key_pk", ["user_id", "version"]) + .addForeignKeyConstraint( + "hmac_secret_key_fk01", + ["user_id", "master_encryption_key_version"], + "master_encryption_key", + ["user_id", "version"], + ) + .execute(); + await db.schema + .createTable("hmac_secret_key_log") + .addColumn("id", "integer", (col) => col.primaryKey().generatedAlwaysAsIdentity()) + .addColumn("user_id", "integer", (col) => col.references("user.id").notNull()) + .addColumn("hmac_secret_key_version", "integer", (col) => col.notNull()) + .addColumn("timestamp", "timestamp(3)", (col) => col.notNull()) + .addColumn("action", "text", (col) => col.notNull()) + .addColumn("action_by", "integer", (col) => col.references("client.id")) + .addForeignKeyConstraint( + "hmac_secret_key_log_fk01", + ["user_id", "hmac_secret_key_version"], + "hmac_secret_key", + ["user_id", "version"], + ) + .execute(); + + // file.ts + await db.schema + .createTable("directory") + .addColumn("id", "integer", (col) => col.primaryKey().generatedAlwaysAsIdentity()) + .addColumn("parent_id", "integer", (col) => col.references("directory.id")) + .addColumn("user_id", "integer", (col) => col.references("user.id").notNull()) + .addColumn("master_encryption_key_version", "integer", (col) => col.notNull()) + .addColumn("encrypted_data_encryption_key", "text", (col) => col.unique().notNull()) + .addColumn("data_encryption_key_version", "timestamp(3)", (col) => col.notNull()) + .addColumn("encrypted_name", "json", (col) => col.notNull()) + .addForeignKeyConstraint( + "directory_fk01", + ["user_id", "master_encryption_key_version"], + "master_encryption_key", + ["user_id", "version"], + ) + .execute(); + await db.schema + .createTable("directory_log") + .addColumn("id", "integer", (col) => col.primaryKey().generatedAlwaysAsIdentity()) + .addColumn("directory_id", "integer", (col) => + col.references("directory.id").onDelete("cascade").notNull(), + ) + .addColumn("timestamp", "timestamp(3)", (col) => col.notNull()) + .addColumn("action", "text", (col) => col.notNull()) + .addColumn("new_name", "json") + .execute(); + await db.schema + .createTable("file") + .addColumn("id", "integer", (col) => col.primaryKey().generatedAlwaysAsIdentity()) + .addColumn("parent_id", "integer", (col) => col.references("directory.id")) + .addColumn("user_id", "integer", (col) => col.references("user.id").notNull()) + .addColumn("path", "text", (col) => col.unique().notNull()) + .addColumn("master_encryption_key_version", "integer", (col) => col.notNull()) + .addColumn("encrypted_data_encryption_key", "text", (col) => col.unique().notNull()) + .addColumn("data_encryption_key_version", "timestamp(3)", (col) => col.notNull()) + .addColumn("hmac_secret_key_version", "integer") + .addColumn("content_hmac", "text") + .addColumn("content_type", "text", (col) => col.notNull()) + .addColumn("encrypted_content_iv", "text", (col) => col.notNull()) + .addColumn("encrypted_content_hash", "text", (col) => col.notNull()) + .addColumn("encrypted_name", "json", (col) => col.notNull()) + .addColumn("encrypted_created_at", "json") + .addColumn("encrypted_last_modified_at", "json", (col) => col.notNull()) + .addForeignKeyConstraint( + "file_fk01", + ["user_id", "master_encryption_key_version"], + "master_encryption_key", + ["user_id", "version"], + ) + .addForeignKeyConstraint( + "file_fk02", + ["user_id", "hmac_secret_key_version"], + "hmac_secret_key", + ["user_id", "version"], + ) + .execute(); + await db.schema + .createTable("file_log") + .addColumn("id", "integer", (col) => col.primaryKey().generatedAlwaysAsIdentity()) + .addColumn("file_id", "integer", (col) => + col.references("file.id").onDelete("cascade").notNull(), + ) + .addColumn("timestamp", "timestamp(3)", (col) => col.notNull()) + .addColumn("action", "text", (col) => col.notNull()) + .addColumn("new_name", "json") + .execute(); +}; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export const down = async (db: Kysely) => { + await db.schema.dropTable("file_log").execute(); + await db.schema.dropTable("file").execute(); + await db.schema.dropTable("directory_log").execute(); + await db.schema.dropTable("directory").execute(); + await db.schema.dropTable("hmac_secret_key_log").execute(); + await db.schema.dropTable("hmac_secret_key").execute(); + await db.schema.dropTable("client_master_encryption_key").execute(); + await db.schema.dropTable("master_encryption_key_log").execute(); + await db.schema.dropTable("master_encryption_key").execute(); + await db.schema.dropTable("session_upgrade_challenge").execute(); + await db.schema.dropTable("session").execute(); + await db.schema.dropTable("user_client_challenge").execute(); + await db.schema.dropTable("user_client").execute(); + await db.schema.dropTable("client").execute(); + await db.schema.dropTable("user").execute(); +}; diff --git a/src/lib/server/db/migrations/index.ts b/src/lib/server/db/migrations/index.ts new file mode 100644 index 0000000..6caca84 --- /dev/null +++ b/src/lib/server/db/migrations/index.ts @@ -0,0 +1,5 @@ +import * as Initial1737357000 from "./1737357000-Initial"; + +export default { + "1737357000-Initial": Initial1737357000, +}; diff --git a/src/lib/server/db/schema/client.ts b/src/lib/server/db/schema/client.ts index 1e9eb85..d66e42b 100644 --- a/src/lib/server/db/schema/client.ts +++ b/src/lib/server/db/schema/client.ts @@ -1,61 +1,32 @@ -import { - sqliteTable, - text, - integer, - primaryKey, - foreignKey, - unique, -} from "drizzle-orm/sqlite-core"; -import { user } from "./user"; +import type { ColumnType, Generated } from "kysely"; -export const client = sqliteTable( - "client", - { - id: integer("id").primaryKey({ autoIncrement: true }), - encPubKey: text("encryption_public_key").notNull().unique(), // Base64 - sigPubKey: text("signature_public_key").notNull().unique(), // Base64 - }, - (t) => ({ - unq: unique().on(t.encPubKey, t.sigPubKey), - }), -); +interface ClientTable { + id: Generated; + encryption_public_key: string; // Base64 + signature_public_key: string; // Base64 +} -export const userClient = sqliteTable( - "user_client", - { - userId: integer("user_id") - .notNull() - .references(() => user.id), - clientId: integer("client_id") - .notNull() - .references(() => client.id), - state: text("state", { enum: ["challenging", "pending", "active"] }) - .notNull() - .default("challenging"), - }, - (t) => ({ - pk: primaryKey({ columns: [t.userId, t.clientId] }), - }), -); +export type UserClientState = "challenging" | "pending" | "active"; -export const userClientChallenge = sqliteTable( - "user_client_challenge", - { - id: integer("id").primaryKey(), - userId: integer("user_id") - .notNull() - .references(() => user.id), - clientId: integer("client_id") - .notNull() - .references(() => client.id), - answer: text("answer").notNull().unique(), // Base64 - allowedIp: text("allowed_ip").notNull(), - expiresAt: integer("expires_at", { mode: "timestamp_ms" }).notNull(), - }, - (t) => ({ - ref: foreignKey({ - columns: [t.userId, t.clientId], - foreignColumns: [userClient.userId, userClient.clientId], - }), - }), -); +interface UserClientTable { + user_id: number; + client_id: number; + state: ColumnType; +} + +interface UserClientChallengeTable { + id: Generated; + user_id: number; + client_id: number; + answer: string; // Base64 + allowed_ip: string; + expires_at: ColumnType; +} + +declare module "./index" { + interface Database { + client: ClientTable; + user_client: UserClientTable; + user_client_challenge: UserClientChallengeTable; + } +} diff --git a/src/lib/server/db/schema/file.ts b/src/lib/server/db/schema/file.ts index a4d5e64..2e13b4c 100644 --- a/src/lib/server/db/schema/file.ts +++ b/src/lib/server/db/schema/file.ts @@ -1,95 +1,53 @@ -import { sqliteTable, text, integer, primaryKey, foreignKey } from "drizzle-orm/sqlite-core"; -import { category } from "./category"; -import { hsk } from "./hsk"; -import { mek } from "./mek"; -import { user } from "./user"; +import type { ColumnType, Generated } from "kysely"; -const ciphertext = (name: string) => - text(name, { mode: "json" }).$type<{ - ciphertext: string; // Base64 - iv: string; // Base64 - }>(); +export type Ciphertext = { + ciphertext: string; // Base64 + iv: string; // Base64 +}; -export const directory = sqliteTable( - "directory", - { - id: integer("id").primaryKey({ autoIncrement: true }), - parentId: integer("parent_id"), - userId: integer("user_id") - .notNull() - .references(() => user.id), - mekVersion: integer("master_encryption_key_version").notNull(), - encDek: text("encrypted_data_encryption_key").notNull().unique(), // Base64 - dekVersion: integer("data_encryption_key_version", { mode: "timestamp_ms" }).notNull(), - encName: ciphertext("encrypted_name").notNull(), - }, - (t) => ({ - ref1: foreignKey({ - columns: [t.parentId], - foreignColumns: [t.id], - }), - ref2: foreignKey({ - columns: [t.userId, t.mekVersion], - foreignColumns: [mek.userId, mek.version], - }), - }), -); +interface DirectoryTable { + id: Generated; + parent_id: number | null; + user_id: number; + master_encryption_key_version: number; + encrypted_data_encryption_key: string; // Base64 + data_encryption_key_version: Date; + encrypted_name: Ciphertext; +} -export const directoryLog = sqliteTable("directory_log", { - id: integer("id").primaryKey({ autoIncrement: true }), - directoryId: integer("directory_id") - .notNull() - .references(() => directory.id, { onDelete: "cascade" }), - timestamp: integer("timestamp", { mode: "timestamp_ms" }).notNull(), - action: text("action", { enum: ["create", "rename"] }).notNull(), - newName: ciphertext("new_name"), -}); +interface DirectoryLogTable { + id: Generated; + directory_id: number; + timestamp: ColumnType; + action: "create" | "rename"; + new_name: Ciphertext | null; +} -export const file = sqliteTable( - "file", - { - id: integer("id").primaryKey({ autoIncrement: true }), - parentId: integer("parent_id").references(() => directory.id), - userId: integer("user_id") - .notNull() - .references(() => user.id), - path: text("path").notNull().unique(), - mekVersion: integer("master_encryption_key_version").notNull(), - encDek: text("encrypted_data_encryption_key").notNull().unique(), // Base64 - dekVersion: integer("data_encryption_key_version", { mode: "timestamp_ms" }).notNull(), - hskVersion: integer("hmac_secret_key_version"), - contentHmac: text("content_hmac"), // Base64 - contentType: text("content_type").notNull(), - encContentIv: text("encrypted_content_iv").notNull(), // Base64 - encContentHash: text("encrypted_content_hash").notNull(), // Base64 - encName: ciphertext("encrypted_name").notNull(), - encCreatedAt: ciphertext("encrypted_created_at"), - encLastModifiedAt: ciphertext("encrypted_last_modified_at").notNull(), - }, - (t) => ({ - ref1: foreignKey({ - columns: [t.userId, t.mekVersion], - foreignColumns: [mek.userId, mek.version], - }), - ref2: foreignKey({ - columns: [t.userId, t.hskVersion], - foreignColumns: [hsk.userId, hsk.version], - }), - }), -); +interface FileTable { + id: Generated; + parent_id: number | null; + user_id: number; + path: string; + master_encryption_key_version: number; + encrypted_data_encryption_key: string; // Base64 + data_encryption_key_version: Date; + hmac_secret_key_version: number | null; + content_hmac: string | null; // Base64 + content_type: string; + encrypted_content_iv: string; // Base64 + encrypted_content_hash: string; // Base64 + encrypted_name: Ciphertext; + encrypted_created_at: Ciphertext | null; + encrypted_last_modified_at: Ciphertext; +} -export const fileLog = sqliteTable("file_log", { - id: integer("id").primaryKey({ autoIncrement: true }), - fileId: integer("file_id") - .notNull() - .references(() => file.id, { onDelete: "cascade" }), - timestamp: integer("timestamp", { mode: "timestamp_ms" }).notNull(), - action: text("action", { - enum: ["create", "rename", "addToCategory", "removeFromCategory"], - }).notNull(), - newName: ciphertext("new_name"), - categoryId: integer("category_id").references(() => category.id, { onDelete: "set null" }), -}); +interface FileLogTable { + id: Generated; + file_id: number; + timestamp: ColumnType; + action: "create" | "rename"; + new_name: Ciphertext | null; +} export const fileCategory = sqliteTable( "file_category", @@ -107,3 +65,12 @@ export const fileCategory = sqliteTable( }), }), ); + +declare module "./index" { + interface Database { + directory: DirectoryTable; + directory_log: DirectoryLogTable; + file: FileTable; + file_log: FileLogTable; + } +} diff --git a/src/lib/server/db/schema/hsk.ts b/src/lib/server/db/schema/hsk.ts index 51f25cc..71457b0 100644 --- a/src/lib/server/db/schema/hsk.ts +++ b/src/lib/server/db/schema/hsk.ts @@ -1,44 +1,27 @@ -import { sqliteTable, text, integer, primaryKey, foreignKey } from "drizzle-orm/sqlite-core"; -import { client } from "./client"; -import { mek } from "./mek"; -import { user } from "./user"; +import type { ColumnType, Generated } from "kysely"; -export const hsk = sqliteTable( - "hmac_secret_key", - { - userId: integer("user_id") - .notNull() - .references(() => user.id), - version: integer("version").notNull(), - state: text("state", { enum: ["active"] }).notNull(), - mekVersion: integer("master_encryption_key_version").notNull(), - encHsk: text("encrypted_key").notNull().unique(), // Base64 - }, - (t) => ({ - pk: primaryKey({ columns: [t.userId, t.version] }), - ref: foreignKey({ - columns: [t.userId, t.mekVersion], - foreignColumns: [mek.userId, mek.version], - }), - }), -); +export type HskState = "active"; -export const hskLog = sqliteTable( - "hmac_secret_key_log", - { - id: integer("id").primaryKey({ autoIncrement: true }), - userId: integer("user_id") - .notNull() - .references(() => user.id), - hskVersion: integer("hmac_secret_key_version").notNull(), - timestamp: integer("timestamp", { mode: "timestamp_ms" }).notNull(), - action: text("action", { enum: ["create"] }).notNull(), - actionBy: integer("action_by").references(() => client.id), - }, - (t) => ({ - ref: foreignKey({ - columns: [t.userId, t.hskVersion], - foreignColumns: [hsk.userId, hsk.version], - }), - }), -); +interface HskTable { + user_id: number; + version: number; + state: HskState; + master_encryption_key_version: number; + encrypted_key: string; // Base64 +} + +interface HskLogTable { + id: Generated; + user_id: number; + hmac_secret_key_version: number; + timestamp: ColumnType; + action: "create"; + action_by: number | null; +} + +declare module "./index" { + interface Database { + hmac_secret_key: HskTable; + hmac_secret_key_log: HskLogTable; + } +} diff --git a/src/lib/server/db/schema/index.ts b/src/lib/server/db/schema/index.ts index 13aff6b..00c72a7 100644 --- a/src/lib/server/db/schema/index.ts +++ b/src/lib/server/db/schema/index.ts @@ -5,3 +5,6 @@ export * from "./hsk"; export * from "./mek"; export * from "./session"; export * from "./user"; + +// eslint-disable-next-line @typescript-eslint/no-empty-object-type +export interface Database {} diff --git a/src/lib/server/db/schema/mek.ts b/src/lib/server/db/schema/mek.ts index e496d9e..d1b3c76 100644 --- a/src/lib/server/db/schema/mek.ts +++ b/src/lib/server/db/schema/mek.ts @@ -1,60 +1,34 @@ -import { sqliteTable, text, integer, primaryKey, foreignKey } from "drizzle-orm/sqlite-core"; -import { client } from "./client"; -import { user } from "./user"; +import type { ColumnType, Generated } from "kysely"; -export const mek = sqliteTable( - "master_encryption_key", - { - userId: integer("user_id") - .notNull() - .references(() => user.id), - version: integer("version").notNull(), - state: text("state", { enum: ["active", "retired", "dead"] }).notNull(), - retiredAt: integer("retired_at", { mode: "timestamp_ms" }), - }, - (t) => ({ - pk: primaryKey({ columns: [t.userId, t.version] }), - }), -); +export type MekState = "active" | "retired" | "dead"; -export const mekLog = sqliteTable( - "master_encryption_key_log", - { - id: integer("id").primaryKey({ autoIncrement: true }), - userId: integer("user_id") - .notNull() - .references(() => user.id), - mekVersion: integer("master_encryption_key_version").notNull(), - timestamp: integer("timestamp", { mode: "timestamp_ms" }).notNull(), - action: text("action", { enum: ["create"] }).notNull(), - actionBy: integer("action_by").references(() => client.id), - }, - (t) => ({ - ref: foreignKey({ - columns: [t.userId, t.mekVersion], - foreignColumns: [mek.userId, mek.version], - }), - }), -); +interface MekTable { + user_id: number; + version: number; + state: MekState; +} -export const clientMek = sqliteTable( - "client_master_encryption_key", - { - userId: integer("user_id") - .notNull() - .references(() => user.id), - clientId: integer("client_id") - .notNull() - .references(() => client.id), - mekVersion: integer("version").notNull(), - encMek: text("encrypted_key").notNull(), // Base64 - encMekSig: text("encrypted_key_signature").notNull(), // Base64 - }, - (t) => ({ - pk: primaryKey({ columns: [t.userId, t.clientId, t.mekVersion] }), - ref: foreignKey({ - columns: [t.userId, t.mekVersion], - foreignColumns: [mek.userId, mek.version], - }), - }), -); +interface MekLogTable { + id: Generated; + user_id: number; + master_encryption_key_version: number; + timestamp: ColumnType; + action: "create"; + action_by: number | null; +} + +interface ClientMekTable { + user_id: number; + client_id: number; + version: number; + encrypted_key: string; // Base64 + encrypted_key_signature: string; // Base64 +} + +declare module "./index" { + interface Database { + master_encryption_key: MekTable; + master_encryption_key_log: MekLogTable; + client_master_encryption_key: ClientMekTable; + } +} diff --git a/src/lib/server/db/schema/session.ts b/src/lib/server/db/schema/session.ts index 5f2129d..301a879 100644 --- a/src/lib/server/db/schema/session.ts +++ b/src/lib/server/db/schema/session.ts @@ -1,35 +1,27 @@ -import { sqliteTable, text, integer, unique } from "drizzle-orm/sqlite-core"; -import { client } from "./client"; -import { user } from "./user"; +import type { ColumnType, Generated } from "kysely"; -export const session = sqliteTable( - "session", - { - id: text("id").notNull().primaryKey(), - userId: integer("user_id") - .notNull() - .references(() => user.id), - clientId: integer("client_id").references(() => client.id), - createdAt: integer("created_at", { mode: "timestamp_ms" }).notNull(), - lastUsedAt: integer("last_used_at", { mode: "timestamp_ms" }).notNull(), - lastUsedByIp: text("last_used_by_ip"), - lastUsedByUserAgent: text("last_used_by_user_agent"), - }, - (t) => ({ - unq: unique().on(t.userId, t.clientId), - }), -); +interface SessionTable { + id: string; + user_id: number; + client_id: number | null; + created_at: ColumnType; + last_used_at: Date; + last_used_by_ip: string | null; + last_used_by_agent: string | null; +} -export const sessionUpgradeChallenge = sqliteTable("session_upgrade_challenge", { - id: integer("id").primaryKey(), - sessionId: text("session_id") - .notNull() - .references(() => session.id) - .unique(), - clientId: integer("client_id") - .notNull() - .references(() => client.id), - answer: text("answer").notNull().unique(), // Base64 - allowedIp: text("allowed_ip").notNull(), - expiresAt: integer("expires_at", { mode: "timestamp_ms" }).notNull(), -}); +interface SessionUpgradeChallengeTable { + id: Generated; + session_id: string; + client_id: number; + answer: string; // Base64 + allowed_ip: string; + expires_at: ColumnType; +} + +declare module "./index" { + interface Database { + session: SessionTable; + session_upgrade_challenge: SessionUpgradeChallengeTable; + } +} diff --git a/src/lib/server/db/schema/user.ts b/src/lib/server/db/schema/user.ts index c98fa01..a5f111f 100644 --- a/src/lib/server/db/schema/user.ts +++ b/src/lib/server/db/schema/user.ts @@ -1,8 +1,14 @@ -import { sqliteTable, text, integer } from "drizzle-orm/sqlite-core"; +import type { Generated } from "kysely"; -export const user = sqliteTable("user", { - id: integer("id").primaryKey({ autoIncrement: true }), - email: text("email").notNull().unique(), - password: text("password").notNull(), - nickname: text("nickname").notNull(), -}); +interface UserTable { + id: Generated; + email: string; + nickname: string; + password: string; +} + +declare module "./index" { + interface Database { + user: UserTable; + } +} diff --git a/src/lib/server/db/session.ts b/src/lib/server/db/session.ts index 819dd86..727f795 100644 --- a/src/lib/server/db/session.ts +++ b/src/lib/server/db/session.ts @@ -1,30 +1,31 @@ -import { SqliteError } from "better-sqlite3"; -import { and, eq, ne, gt, lte, isNull } from "drizzle-orm"; +import pg from "pg"; import env from "$lib/server/loadenv"; -import db from "./drizzle"; import { IntegrityError } from "./error"; -import { session, sessionUpgradeChallenge } from "./schema"; +import db from "./kysely"; export const createSession = async ( userId: number, clientId: number | null, sessionId: string, ip: string | null, - userAgent: string | null, + agent: string | null, ) => { try { const now = new Date(); - await db.insert(session).values({ - id: sessionId, - userId, - clientId, - createdAt: now, - lastUsedAt: now, - lastUsedByIp: ip || null, - lastUsedByUserAgent: userAgent || null, - }); + await db + .insertInto("session") + .values({ + id: sessionId, + user_id: userId, + client_id: clientId, + created_at: now, + last_used_at: now, + last_used_by_ip: ip || null, + last_used_by_agent: agent || null, + }) + .execute(); } catch (e) { - if (e instanceof SqliteError && e.code === "SQLITE_CONSTRAINT_UNIQUE") { + if (e instanceof pg.DatabaseError && e.code === "23505") { throw new IntegrityError("Session already exists"); } throw e; @@ -34,49 +35,55 @@ export const createSession = async ( export const refreshSession = async ( sessionId: string, ip: string | null, - userAgent: string | null, + agent: string | null, ) => { const now = new Date(); - const sessions = await db - .update(session) + const session = await db + .updateTable("session") .set({ - lastUsedAt: now, - lastUsedByIp: ip || undefined, - lastUsedByUserAgent: userAgent || undefined, + last_used_at: now, + last_used_by_ip: ip !== "" ? ip : undefined, // Don't update if empty + last_used_by_agent: agent !== "" ? agent : undefined, // Don't update if empty }) - .where( - and( - eq(session.id, sessionId), - gt(session.lastUsedAt, new Date(now.getTime() - env.session.exp)), - ), - ) - .returning({ userId: session.userId, clientId: session.clientId }); - if (!sessions[0]) { + .where("id", "=", sessionId) + .where("last_used_at", ">", new Date(now.getTime() - env.session.exp)) + .returning(["user_id", "client_id"]) + .executeTakeFirst(); + if (!session) { throw new IntegrityError("Session not found"); } - return sessions[0]; + return { userId: session.user_id, clientId: session.client_id }; }; export const upgradeSession = async (sessionId: string, clientId: number) => { const res = await db - .update(session) - .set({ clientId }) - .where(and(eq(session.id, sessionId), isNull(session.clientId))); - if (res.changes === 0) { + .updateTable("session") + .set({ client_id: clientId }) + .where("id", "=", sessionId) + .where("client_id", "is", null) + .executeTakeFirst(); + if (res.numUpdatedRows === 0n) { throw new IntegrityError("Session not found"); } }; export const deleteSession = async (sessionId: string) => { - await db.delete(session).where(eq(session.id, sessionId)); + await db.deleteFrom("session").where("id", "=", sessionId).execute(); }; export const deleteAllOtherSessions = async (userId: number, sessionId: string) => { - await db.delete(session).where(and(eq(session.userId, userId), ne(session.id, sessionId))); + await db + .deleteFrom("session") + .where("id", "!=", sessionId) + .where("user_id", "=", userId) + .execute(); }; export const cleanupExpiredSessions = async () => { - await db.delete(session).where(lte(session.lastUsedAt, new Date(Date.now() - env.session.exp))); + await db + .deleteFrom("session") + .where("last_used_at", "<=", new Date(Date.now() - env.session.exp)) + .execute(); }; export const registerSessionUpgradeChallenge = async ( @@ -87,15 +94,18 @@ export const registerSessionUpgradeChallenge = async ( expiresAt: Date, ) => { try { - await db.insert(sessionUpgradeChallenge).values({ - sessionId, - clientId, - answer, - allowedIp, - expiresAt, - }); + await db + .insertInto("session_upgrade_challenge") + .values({ + session_id: sessionId, + client_id: clientId, + answer, + allowed_ip: allowedIp, + expires_at: expiresAt, + }) + .execute(); } catch (e) { - if (e instanceof SqliteError && e.code === "SQLITE_CONSTRAINT_UNIQUE") { + if (e instanceof pg.DatabaseError && e.code === "23505") { throw new IntegrityError("Challenge already registered"); } throw e; @@ -107,22 +117,17 @@ export const consumeSessionUpgradeChallenge = async ( answer: string, ip: string, ) => { - const challenges = await db - .delete(sessionUpgradeChallenge) - .where( - and( - eq(sessionUpgradeChallenge.sessionId, sessionId), - eq(sessionUpgradeChallenge.answer, answer), - eq(sessionUpgradeChallenge.allowedIp, ip), - gt(sessionUpgradeChallenge.expiresAt, new Date()), - ), - ) - .returning({ clientId: sessionUpgradeChallenge.clientId }); - return challenges[0] ?? null; + const challenge = await db + .deleteFrom("session_upgrade_challenge") + .where("session_id", "=", sessionId) + .where("answer", "=", answer) + .where("allowed_ip", "=", ip) + .where("expires_at", ">", new Date()) + .returning("client_id") + .executeTakeFirst(); + return challenge ? { clientId: challenge.client_id } : null; }; export const cleanupExpiredSessionUpgradeChallenges = async () => { - await db - .delete(sessionUpgradeChallenge) - .where(lte(sessionUpgradeChallenge.expiresAt, new Date())); + await db.deleteFrom("session_upgrade_challenge").where("expires_at", "<=", new Date()).execute(); }; diff --git a/src/lib/server/db/user.ts b/src/lib/server/db/user.ts index d970438..3964a94 100644 --- a/src/lib/server/db/user.ts +++ b/src/lib/server/db/user.ts @@ -1,21 +1,36 @@ -import { eq } from "drizzle-orm"; -import db from "./drizzle"; -import { user } from "./schema"; +import db from "./kysely"; + +interface User { + id: number; + email: string; + nickname: string; + password: string; +} export const getUser = async (userId: number) => { - const users = await db.select().from(user).where(eq(user.id, userId)).limit(1); - return users[0] ?? null; + const user = await db + .selectFrom("user") + .selectAll() + .where("id", "=", userId) + .limit(1) + .executeTakeFirst(); + return user ? (user satisfies User) : null; }; export const getUserByEmail = async (email: string) => { - const users = await db.select().from(user).where(eq(user.email, email)).limit(1); - return users[0] ?? null; -}; - -export const setUserPassword = async (userId: number, password: string) => { - await db.update(user).set({ password }).where(eq(user.id, userId)); + const user = await db + .selectFrom("user") + .selectAll() + .where("email", "=", email) + .limit(1) + .executeTakeFirst(); + return user ? (user satisfies User) : null; }; export const setUserNickname = async (userId: number, nickname: string) => { - await db.update(user).set({ nickname }).where(eq(user.id, userId)); + await db.updateTable("user").set({ nickname }).where("id", "=", userId).execute(); +}; + +export const setUserPassword = async (userId: number, password: string) => { + await db.updateTable("user").set({ password }).where("id", "=", userId).execute(); }; diff --git a/src/lib/server/loadenv.ts b/src/lib/server/loadenv.ts index 01e442a..d6f4675 100644 --- a/src/lib/server/loadenv.ts +++ b/src/lib/server/loadenv.ts @@ -3,11 +3,19 @@ import { building } from "$app/environment"; import { env } from "$env/dynamic/private"; if (!building) { + if (!env.DATABASE_PASSWORD) throw new Error("DATABASE_PASSWORD not set"); if (!env.SESSION_SECRET) throw new Error("SESSION_SECRET not set"); } export default { - databaseUrl: env.DATABASE_URL || "local.db", + nodeEnv: env.NODE_ENV || "development", + database: { + host: env.DATABASE_HOST, + port: env.DATABASE_PORT ? parseInt(env.DATABASE_PORT, 10) : undefined, + user: env.DATABASE_USER, + password: env.DATABASE_PASSWORD!, + name: env.DATABASE_NAME, + }, session: { secret: env.SESSION_SECRET!, exp: ms(env.SESSION_EXPIRES || "14d"), diff --git a/src/lib/server/modules/mek.ts b/src/lib/server/modules/mek.ts index d65ef0a..1605d75 100644 --- a/src/lib/server/modules/mek.ts +++ b/src/lib/server/modules/mek.ts @@ -21,5 +21,5 @@ export const verifyClientEncMekSig = async ( } const data = JSON.stringify({ version, key: encMek }); - return verifySignature(Buffer.from(data), encMekSig, userClient.client.sigPubKey); + return verifySignature(Buffer.from(data), encMekSig, userClient.sigPubKey); }; diff --git a/src/lib/server/services/client.ts b/src/lib/server/services/client.ts index b5b0209..0d0b82d 100644 --- a/src/lib/server/services/client.ts +++ b/src/lib/server/services/client.ts @@ -63,7 +63,7 @@ export const registerUserClient = async ( } try { - const clientId = await createClient(encPubKey, sigPubKey, userId); + const { clientId } = await createClient(encPubKey, sigPubKey, userId); return { challenge: await createUserClientChallenge(ip, userId, clientId, encPubKey) }; } catch (e) { if (e instanceof IntegrityError && e.message === "Public key(s) already registered") { diff --git a/src/lib/server/services/directory.ts b/src/lib/server/services/directory.ts index 4dc14ce..be795b0 100644 --- a/src/lib/server/services/directory.ts +++ b/src/lib/server/services/directory.ts @@ -8,8 +8,9 @@ import { setDirectoryEncName, unregisterDirectory, getAllFilesByParent, - type NewDirectoryParams, + type NewDirectory, } from "$lib/server/db/file"; +import type { Ciphertext } from "$lib/server/db/schema"; export const getDirectoryInformation = async (userId: number, directoryId: "root" | number) => { const directory = directoryId !== "root" ? await getDirectory(userId, directoryId) : undefined; @@ -53,11 +54,10 @@ export const renameDirectory = async ( userId: number, directoryId: number, dekVersion: Date, - newEncName: string, - newEncNameIv: string, + newEncName: Ciphertext, ) => { try { - await setDirectoryEncName(userId, directoryId, dekVersion, newEncName, newEncNameIv); + await setDirectoryEncName(userId, directoryId, dekVersion, newEncName); } catch (e) { if (e instanceof IntegrityError) { if (e.message === "Directory not found") { @@ -70,7 +70,7 @@ export const renameDirectory = async ( } }; -export const createDirectory = async (params: NewDirectoryParams) => { +export const createDirectory = async (params: NewDirectory) => { const oneMinuteAgo = new Date(Date.now() - 60 * 1000); const oneMinuteLater = new Date(Date.now() + 60 * 1000); if (params.dekVersion <= oneMinuteAgo || params.dekVersion >= oneMinuteLater) { diff --git a/src/lib/server/services/file.ts b/src/lib/server/services/file.ts index ea01f16..0f2d371 100644 --- a/src/lib/server/services/file.ts +++ b/src/lib/server/services/file.ts @@ -13,8 +13,9 @@ import { getFile, setFileEncName, unregisterFile, - type NewFileParams, + type NewFile, } from "$lib/server/db/file"; +import type { Ciphertext } from "$lib/server/db/schema"; import env from "$lib/server/loadenv"; export const getFileInformation = async (userId: number, fileId: number) => { @@ -38,8 +39,8 @@ export const getFileInformation = async (userId: number, fileId: number) => { export const deleteFile = async (userId: number, fileId: number) => { try { - const filePath = await unregisterFile(userId, fileId); - unlink(filePath); // Intended + const { path } = await unregisterFile(userId, fileId); + unlink(path); // Intended } catch (e) { if (e instanceof IntegrityError && e.message === "File not found") { error(404, "Invalid file id"); @@ -65,11 +66,10 @@ export const renameFile = async ( userId: number, fileId: number, dekVersion: Date, - newEncName: string, - newEncNameIv: string, + newEncName: Ciphertext, ) => { try { - await setFileEncName(userId, fileId, dekVersion, newEncName, newEncNameIv); + await setFileEncName(userId, fileId, dekVersion, newEncName); } catch (e) { if (e instanceof IntegrityError) { if (e.message === "File not found") { @@ -96,7 +96,7 @@ const safeUnlink = async (path: string) => { }; export const uploadFile = async ( - params: Omit, + params: Omit, encContentStream: Readable, encContentHash: Promise, ) => { diff --git a/src/lib/server/services/mek.ts b/src/lib/server/services/mek.ts index e0deeb0..097906a 100644 --- a/src/lib/server/services/mek.ts +++ b/src/lib/server/services/mek.ts @@ -7,11 +7,11 @@ import { verifyClientEncMekSig } from "$lib/server/modules/mek"; export const getClientMekList = async (userId: number, clientId: number) => { const clientMeks = await getAllValidClientMeks(userId, clientId); return { - encMeks: clientMeks.map((clientMek) => ({ - version: clientMek.master_encryption_key.version, - state: clientMek.master_encryption_key.state as "active" | "retired", - encMek: clientMek.client_master_encryption_key.encMek, - encMekSig: clientMek.client_master_encryption_key.encMekSig, + encMeks: clientMeks.map(({ version, state, encMek, encMekSig }) => ({ + version, + state, + encMek, + encMekSig, })), }; }; diff --git a/src/routes/api/directory/[id]/rename/+server.ts b/src/routes/api/directory/[id]/rename/+server.ts index 0d95e13..cc50b2f 100644 --- a/src/routes/api/directory/[id]/rename/+server.ts +++ b/src/routes/api/directory/[id]/rename/+server.ts @@ -20,6 +20,6 @@ export const POST: RequestHandler = async ({ locals, params, request }) => { if (!bodyZodRes.success) error(400, "Invalid request body"); const { dekVersion, name, nameIv } = bodyZodRes.data; - await renameDirectory(userId, id, new Date(dekVersion), name, nameIv); + await renameDirectory(userId, id, new Date(dekVersion), { ciphertext: name, iv: nameIv }); return text("Directory renamed", { headers: { "Content-Type": "text/plain" } }); }; diff --git a/src/routes/api/directory/create/+server.ts b/src/routes/api/directory/create/+server.ts index 07711fc..7c65436 100644 --- a/src/routes/api/directory/create/+server.ts +++ b/src/routes/api/directory/create/+server.ts @@ -17,8 +17,7 @@ export const POST: RequestHandler = async ({ locals, request }) => { mekVersion, encDek: dek, dekVersion: new Date(dekVersion), - encName: name, - encNameIv: nameIv, + encName: { ciphertext: name, iv: nameIv }, }); return text("Directory created", { headers: { "Content-Type": "text/plain" } }); }; diff --git a/src/routes/api/file/[id]/rename/+server.ts b/src/routes/api/file/[id]/rename/+server.ts index c6748a0..343f146 100644 --- a/src/routes/api/file/[id]/rename/+server.ts +++ b/src/routes/api/file/[id]/rename/+server.ts @@ -20,6 +20,6 @@ export const POST: RequestHandler = async ({ locals, params, request }) => { if (!bodyZodRes.success) error(400, "Invalid request body"); const { dekVersion, name, nameIv } = bodyZodRes.data; - await renameFile(userId, id, new Date(dekVersion), name, nameIv); + await renameFile(userId, id, new Date(dekVersion), { ciphertext: name, iv: nameIv }); return text("File renamed", { headers: { "Content-Type": "text/plain" } }); }; diff --git a/src/routes/api/file/upload/+server.ts b/src/routes/api/file/upload/+server.ts index a69df0c..b54a542 100644 --- a/src/routes/api/file/upload/+server.ts +++ b/src/routes/api/file/upload/+server.ts @@ -40,12 +40,9 @@ const parseFileMetadata = (userId: number, json: string) => { contentHmac, contentType, encContentIv: contentIv, - encName: name, - encNameIv: nameIv, - encCreatedAt: createdAt ?? null, - encCreatedAtIv: createdAtIv ?? null, - encLastModifiedAt: lastModifiedAt, - encLastModifiedAtIv: lastModifiedAtIv, + encName: { ciphertext: name, iv: nameIv }, + encCreatedAt: createdAt && createdAtIv ? { ciphertext: createdAt, iv: createdAtIv } : null, + encLastModifiedAt: { ciphertext: lastModifiedAt, iv: lastModifiedAtIv }, } satisfies FileMetadata; };