From 5ae34ed690429c537cc6247c1588a14b875d270a Mon Sep 17 00:00:00 2001 From: jhhong0571 <105486482+jhhong0571@users.noreply.github.com> Date: Tue, 9 Sep 2025 23:23:38 +0900 Subject: [PATCH 1/5] Add files via upload --- .../migration.sql | 26 ++++++ .../20250801053712_init/migration.sql | 36 +++++++++ .../migration.sql | 10 +++ .../prisma/migrations/migration_lock.toml | 3 + 4-Sprint-Misson/prisma/schema.prisma | 81 +++++++++++++++++++ 5 files changed, 156 insertions(+) create mode 100644 4-Sprint-Misson/prisma/migrations/20250729043254_init_simple_blog/migration.sql create mode 100644 4-Sprint-Misson/prisma/migrations/20250801053712_init/migration.sql create mode 100644 4-Sprint-Misson/prisma/migrations/20250804002706_add_comment_model/migration.sql create mode 100644 4-Sprint-Misson/prisma/migrations/migration_lock.toml create mode 100644 4-Sprint-Misson/prisma/schema.prisma diff --git a/4-Sprint-Misson/prisma/migrations/20250729043254_init_simple_blog/migration.sql b/4-Sprint-Misson/prisma/migrations/20250729043254_init_simple_blog/migration.sql new file mode 100644 index 000000000..d5c4624a3 --- /dev/null +++ b/4-Sprint-Misson/prisma/migrations/20250729043254_init_simple_blog/migration.sql @@ -0,0 +1,26 @@ +-- CreateTable +CREATE TABLE "User" ( + "id" SERIAL NOT NULL, + "nickname" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "User_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "Post" ( + "id" SERIAL NOT NULL, + "title" TEXT NOT NULL, + "content" TEXT NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + "views" INTEGER NOT NULL DEFAULT 0, + + CONSTRAINT "Post_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE UNIQUE INDEX "User_nickname_key" ON "User"("nickname"); + +-- CreateIndex +CREATE UNIQUE INDEX "Post_title_key" ON "Post"("title"); diff --git a/4-Sprint-Misson/prisma/migrations/20250801053712_init/migration.sql b/4-Sprint-Misson/prisma/migrations/20250801053712_init/migration.sql new file mode 100644 index 000000000..4bc4ab5ca --- /dev/null +++ b/4-Sprint-Misson/prisma/migrations/20250801053712_init/migration.sql @@ -0,0 +1,36 @@ +/* + Warnings: + + - You are about to drop the `Post` table. If the table is not empty, all the data it contains will be lost. + - You are about to drop the `User` table. If the table is not empty, all the data it contains will be lost. + +*/ +-- DropTable +DROP TABLE "public"."Post"; + +-- DropTable +DROP TABLE "public"."User"; + +-- CreateTable +CREATE TABLE "public"."Product" ( + "id" SERIAL NOT NULL, + "name" TEXT NOT NULL, + "description" TEXT NOT NULL, + "price" INTEGER NOT NULL, + "tags" TEXT NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "Product_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "public"."Article" ( + "id" SERIAL NOT NULL, + "title" TEXT NOT NULL, + "content" TEXT NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "Article_pkey" PRIMARY KEY ("id") +); diff --git a/4-Sprint-Misson/prisma/migrations/20250804002706_add_comment_model/migration.sql b/4-Sprint-Misson/prisma/migrations/20250804002706_add_comment_model/migration.sql new file mode 100644 index 000000000..d477f7171 --- /dev/null +++ b/4-Sprint-Misson/prisma/migrations/20250804002706_add_comment_model/migration.sql @@ -0,0 +1,10 @@ +-- CreateTable +CREATE TABLE "public"."comment" ( + "id" SERIAL NOT NULL, + "content" TEXT NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "productId" INTEGER, + "articleId" INTEGER, + + CONSTRAINT "comment_pkey" PRIMARY KEY ("id") +); diff --git a/4-Sprint-Misson/prisma/migrations/migration_lock.toml b/4-Sprint-Misson/prisma/migrations/migration_lock.toml new file mode 100644 index 000000000..044d57cdb --- /dev/null +++ b/4-Sprint-Misson/prisma/migrations/migration_lock.toml @@ -0,0 +1,3 @@ +# Please do not edit this file manually +# It should be added in your version-control system (e.g., Git) +provider = "postgresql" diff --git a/4-Sprint-Misson/prisma/schema.prisma b/4-Sprint-Misson/prisma/schema.prisma new file mode 100644 index 000000000..1645bf62a --- /dev/null +++ b/4-Sprint-Misson/prisma/schema.prisma @@ -0,0 +1,81 @@ +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") +} + +generator client { + provider = "prisma-client-js" +} + +model User { + id Int @id @default(autoincrement()) + email String @unique + nickname String + image String? + password String + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + refreshToken String? //토큰 + products Product[] + comments comment[] + articles Article[] + likes Like[] +} + + +model Product { + id Int @id @default(autoincrement()) + name String + description String + price Float + tags String? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + userId Int + user User @relation(fields: [userId], references: [id]) + comments comment[] + likes Like[] +} + +model Article { + id Int @id @default(autoincrement()) + title String + content String + userId Int + createdAt DateTime @default(now()) + updatedAt DateTime @default(now()) + + user User @relation(fields: [userId], references: [id]) + comments comment[] + likes Like[] +} + +model comment { + id Int @id @default(autoincrement()) + content String + createdAt DateTime @default(now()) + userId Int + user User @relation(fields: [userId], references: [id]) + productId Int? + product Product? @relation(fields: [productId], references: [id]) + articleId Int? + article Article? @relation(fields: [articleId], references: [id]) +} + +model Like { + id Int @id @default(autoincrement()) + userId Int + productId Int? + articleId Int? + createdAt DateTime @default(now()) + + user User @relation(fields: [userId], references: [id]) + product Product? @relation(fields: [productId], references: [id]) + article Article? @relation(fields: [articleId], references: [id]) + + @@unique([userId, productId]) + @@unique([userId, articleId]) +} + From d90af270c8d89a564cf23653c02efe816d358954 Mon Sep 17 00:00:00 2001 From: jhhong0571 <105486482+jhhong0571@users.noreply.github.com> Date: Tue, 9 Sep 2025 23:24:31 +0900 Subject: [PATCH 2/5] Delete 4-Sprint-Misson/prisma directory --- .../migration.sql | 26 ------ .../20250801053712_init/migration.sql | 36 --------- .../migration.sql | 10 --- .../prisma/migrations/migration_lock.toml | 3 - 4-Sprint-Misson/prisma/schema.prisma | 81 ------------------- 5 files changed, 156 deletions(-) delete mode 100644 4-Sprint-Misson/prisma/migrations/20250729043254_init_simple_blog/migration.sql delete mode 100644 4-Sprint-Misson/prisma/migrations/20250801053712_init/migration.sql delete mode 100644 4-Sprint-Misson/prisma/migrations/20250804002706_add_comment_model/migration.sql delete mode 100644 4-Sprint-Misson/prisma/migrations/migration_lock.toml delete mode 100644 4-Sprint-Misson/prisma/schema.prisma diff --git a/4-Sprint-Misson/prisma/migrations/20250729043254_init_simple_blog/migration.sql b/4-Sprint-Misson/prisma/migrations/20250729043254_init_simple_blog/migration.sql deleted file mode 100644 index d5c4624a3..000000000 --- a/4-Sprint-Misson/prisma/migrations/20250729043254_init_simple_blog/migration.sql +++ /dev/null @@ -1,26 +0,0 @@ --- CreateTable -CREATE TABLE "User" ( - "id" SERIAL NOT NULL, - "nickname" TEXT, - "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - - CONSTRAINT "User_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "Post" ( - "id" SERIAL NOT NULL, - "title" TEXT NOT NULL, - "content" TEXT NOT NULL, - "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" TIMESTAMP(3) NOT NULL, - "views" INTEGER NOT NULL DEFAULT 0, - - CONSTRAINT "Post_pkey" PRIMARY KEY ("id") -); - --- CreateIndex -CREATE UNIQUE INDEX "User_nickname_key" ON "User"("nickname"); - --- CreateIndex -CREATE UNIQUE INDEX "Post_title_key" ON "Post"("title"); diff --git a/4-Sprint-Misson/prisma/migrations/20250801053712_init/migration.sql b/4-Sprint-Misson/prisma/migrations/20250801053712_init/migration.sql deleted file mode 100644 index 4bc4ab5ca..000000000 --- a/4-Sprint-Misson/prisma/migrations/20250801053712_init/migration.sql +++ /dev/null @@ -1,36 +0,0 @@ -/* - Warnings: - - - You are about to drop the `Post` table. If the table is not empty, all the data it contains will be lost. - - You are about to drop the `User` table. If the table is not empty, all the data it contains will be lost. - -*/ --- DropTable -DROP TABLE "public"."Post"; - --- DropTable -DROP TABLE "public"."User"; - --- CreateTable -CREATE TABLE "public"."Product" ( - "id" SERIAL NOT NULL, - "name" TEXT NOT NULL, - "description" TEXT NOT NULL, - "price" INTEGER NOT NULL, - "tags" TEXT NOT NULL, - "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - - CONSTRAINT "Product_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "public"."Article" ( - "id" SERIAL NOT NULL, - "title" TEXT NOT NULL, - "content" TEXT NOT NULL, - "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - - CONSTRAINT "Article_pkey" PRIMARY KEY ("id") -); diff --git a/4-Sprint-Misson/prisma/migrations/20250804002706_add_comment_model/migration.sql b/4-Sprint-Misson/prisma/migrations/20250804002706_add_comment_model/migration.sql deleted file mode 100644 index d477f7171..000000000 --- a/4-Sprint-Misson/prisma/migrations/20250804002706_add_comment_model/migration.sql +++ /dev/null @@ -1,10 +0,0 @@ --- CreateTable -CREATE TABLE "public"."comment" ( - "id" SERIAL NOT NULL, - "content" TEXT NOT NULL, - "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "productId" INTEGER, - "articleId" INTEGER, - - CONSTRAINT "comment_pkey" PRIMARY KEY ("id") -); diff --git a/4-Sprint-Misson/prisma/migrations/migration_lock.toml b/4-Sprint-Misson/prisma/migrations/migration_lock.toml deleted file mode 100644 index 044d57cdb..000000000 --- a/4-Sprint-Misson/prisma/migrations/migration_lock.toml +++ /dev/null @@ -1,3 +0,0 @@ -# Please do not edit this file manually -# It should be added in your version-control system (e.g., Git) -provider = "postgresql" diff --git a/4-Sprint-Misson/prisma/schema.prisma b/4-Sprint-Misson/prisma/schema.prisma deleted file mode 100644 index 1645bf62a..000000000 --- a/4-Sprint-Misson/prisma/schema.prisma +++ /dev/null @@ -1,81 +0,0 @@ -datasource db { - provider = "postgresql" - url = env("DATABASE_URL") -} - -generator client { - provider = "prisma-client-js" -} - -model User { - id Int @id @default(autoincrement()) - email String @unique - nickname String - image String? - password String - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - - refreshToken String? //토큰 - products Product[] - comments comment[] - articles Article[] - likes Like[] -} - - -model Product { - id Int @id @default(autoincrement()) - name String - description String - price Float - tags String? - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - - userId Int - user User @relation(fields: [userId], references: [id]) - comments comment[] - likes Like[] -} - -model Article { - id Int @id @default(autoincrement()) - title String - content String - userId Int - createdAt DateTime @default(now()) - updatedAt DateTime @default(now()) - - user User @relation(fields: [userId], references: [id]) - comments comment[] - likes Like[] -} - -model comment { - id Int @id @default(autoincrement()) - content String - createdAt DateTime @default(now()) - userId Int - user User @relation(fields: [userId], references: [id]) - productId Int? - product Product? @relation(fields: [productId], references: [id]) - articleId Int? - article Article? @relation(fields: [articleId], references: [id]) -} - -model Like { - id Int @id @default(autoincrement()) - userId Int - productId Int? - articleId Int? - createdAt DateTime @default(now()) - - user User @relation(fields: [userId], references: [id]) - product Product? @relation(fields: [productId], references: [id]) - article Article? @relation(fields: [articleId], references: [id]) - - @@unique([userId, productId]) - @@unique([userId, articleId]) -} - From b8005d9c4c4d099472461c6eade209ef4627944f Mon Sep 17 00:00:00 2001 From: jhhong0571 <105486482+jhhong0571@users.noreply.github.com> Date: Tue, 9 Sep 2025 23:25:15 +0900 Subject: [PATCH 3/5] =?UTF-8?q?=EC=8A=A4=ED=94=84=EB=A6=B0=ED=8A=B8=20?= =?UTF-8?q?=EB=AF=B8=EC=85=98=204?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 4-Sprint-Misson/package-lock.json | 1191 +++++++++++++++++ 4-Sprint-Misson/package.json | 21 + .../migration.sql | 26 + .../20250801053712_init/migration.sql | 36 + .../migration.sql | 10 + .../prisma/migrations/migration_lock.toml | 3 + 4-Sprint-Misson/prisma/schema.prisma | 81 ++ 4-Sprint-Misson/product.js | 148 ++ .../src/controllers/articlesController.js | 140 ++ .../src/controllers/authController.js | 77 ++ .../src/controllers/commentsController.js | 133 ++ .../src/controllers/likeController.js | 70 + .../src/controllers/productController.js | 132 ++ .../src/controllers/usersController.js | 108 ++ 4-Sprint-Misson/src/middlewares/auth.js | 14 + 4-Sprint-Misson/src/routes/articleRoutes.js | 17 + 4-Sprint-Misson/src/routes/authRoutes.js | 17 + 4-Sprint-Misson/src/routes/commentRoutes.js | 20 + 4-Sprint-Misson/src/routes/likeRoutes.js | 17 + 4-Sprint-Misson/src/routes/productRoutes.js | 19 + 4-Sprint-Misson/src/routes/usersRoutes.js | 18 + 4-Sprint-Misson/src/server.js | 22 + .../src/uploads/1753935463073-536058091.png | Bin 0 -> 84440 bytes .../src/uploads/1753935773083-773169808.jpg | Bin 0 -> 7016 bytes 4-Sprint-Misson/src/utils/jwt.js | 24 + 25 files changed, 2344 insertions(+) create mode 100644 4-Sprint-Misson/package-lock.json create mode 100644 4-Sprint-Misson/package.json create mode 100644 4-Sprint-Misson/prisma/migrations/20250729043254_init_simple_blog/migration.sql create mode 100644 4-Sprint-Misson/prisma/migrations/20250801053712_init/migration.sql create mode 100644 4-Sprint-Misson/prisma/migrations/20250804002706_add_comment_model/migration.sql create mode 100644 4-Sprint-Misson/prisma/migrations/migration_lock.toml create mode 100644 4-Sprint-Misson/prisma/schema.prisma create mode 100644 4-Sprint-Misson/product.js create mode 100644 4-Sprint-Misson/src/controllers/articlesController.js create mode 100644 4-Sprint-Misson/src/controllers/authController.js create mode 100644 4-Sprint-Misson/src/controllers/commentsController.js create mode 100644 4-Sprint-Misson/src/controllers/likeController.js create mode 100644 4-Sprint-Misson/src/controllers/productController.js create mode 100644 4-Sprint-Misson/src/controllers/usersController.js create mode 100644 4-Sprint-Misson/src/middlewares/auth.js create mode 100644 4-Sprint-Misson/src/routes/articleRoutes.js create mode 100644 4-Sprint-Misson/src/routes/authRoutes.js create mode 100644 4-Sprint-Misson/src/routes/commentRoutes.js create mode 100644 4-Sprint-Misson/src/routes/likeRoutes.js create mode 100644 4-Sprint-Misson/src/routes/productRoutes.js create mode 100644 4-Sprint-Misson/src/routes/usersRoutes.js create mode 100644 4-Sprint-Misson/src/server.js create mode 100644 4-Sprint-Misson/src/uploads/1753935463073-536058091.png create mode 100644 4-Sprint-Misson/src/uploads/1753935773083-773169808.jpg create mode 100644 4-Sprint-Misson/src/utils/jwt.js diff --git a/4-Sprint-Misson/package-lock.json b/4-Sprint-Misson/package-lock.json new file mode 100644 index 000000000..597ef01a1 --- /dev/null +++ b/4-Sprint-Misson/package-lock.json @@ -0,0 +1,1191 @@ +{ + "name": "3-sprint-misson", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "3-sprint-misson", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "@prisma/client": "^6.13.0", + "bcrypt": "^6.0.0", + "dotenv": "^17.2.1", + "express": "^5.1.0", + "jsonwebtoken": "^9.0.2", + "multer": "^2.0.2" + } + }, + "node_modules/@prisma/client": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/@prisma/client/-/client-6.13.0.tgz", + "integrity": "sha512-8m2+I3dQovkV8CkDMluiwEV1TxV9EXdT6xaCz39O6jYw7mkf5gwfmi+cL4LJsEPwz5tG7sreBwkRpEMJedGYUQ==", + "hasInstallScript": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "peerDependencies": { + "prisma": "*", + "typescript": ">=5.1.0" + }, + "peerDependenciesMeta": { + "prisma": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "license": "MIT", + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/append-field": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", + "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==", + "license": "MIT" + }, + "node_modules/bcrypt": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-6.0.0.tgz", + "integrity": "sha512-cU8v/EGSrnH+HnxV2z0J7/blxH8gq7Xh2JFT6Aroax7UohdmiJJlxApMxtKfuI7z68NvvVcmR78k2LbT6efhRg==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "node-addon-api": "^8.3.0", + "node-gyp-build": "^4.8.4" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/body-parser": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz", + "integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==", + "license": "MIT", + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^1.0.5", + "debug": "^4.4.0", + "http-errors": "^2.0.0", + "iconv-lite": "^0.6.3", + "on-finished": "^2.4.1", + "qs": "^6.14.0", + "raw-body": "^3.0.0", + "type-is": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "license": "BSD-3-Clause" + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "license": "MIT" + }, + "node_modules/busboy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "dependencies": { + "streamsearch": "^1.1.0" + }, + "engines": { + "node": ">=10.16.0" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/concat-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", + "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", + "engines": [ + "node >= 6.0" + ], + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.0.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/content-disposition": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz", + "integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } + }, + "node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/dotenv": { + "version": "17.2.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.1.tgz", + "integrity": "sha512-kQhDYKZecqnM0fCnzI5eIv5L4cAe/iRI+HqMbO/hbRdTAeXDG+M9FjipUxNfbARuEg4iHIbhnhs78BCHNbSxEQ==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz", + "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==", + "license": "MIT", + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.0", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/finalhandler": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz", + "integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "license": "MIT", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-errors/node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "license": "MIT" + }, + "node_modules/jsonwebtoken": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "license": "MIT", + "dependencies": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jwa": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.2.tgz", + "integrity": "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==", + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "^1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "license": "MIT", + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", + "license": "MIT" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", + "license": "MIT" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", + "license": "MIT" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", + "license": "MIT" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "license": "MIT" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", + "license": "MIT" + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", + "license": "MIT" + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz", + "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "license": "MIT", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/multer": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/multer/-/multer-2.0.2.tgz", + "integrity": "sha512-u7f2xaZ/UG8oLXHvtF/oWTRvT44p9ecwBBqTwgJVq0+4BW1g8OW01TyMEGWBHbyMOYVHXslaut7qEQ1meATXgw==", + "license": "MIT", + "dependencies": { + "append-field": "^1.0.0", + "busboy": "^1.6.0", + "concat-stream": "^2.0.0", + "mkdirp": "^0.5.6", + "object-assign": "^4.1.1", + "type-is": "^1.6.18", + "xtend": "^4.0.2" + }, + "engines": { + "node": ">= 10.16.0" + } + }, + "node_modules/multer/node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/multer/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/multer/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/multer/node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/node-addon-api": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.5.0.tgz", + "integrity": "sha512-/bRZty2mXUIFY/xU5HLvveNHlswNJej+RnxBjOMkidWfwZzgTbPG1E3K5TOxRLOR+5hX7bSofy8yf1hZevMS8A==", + "license": "MIT", + "engines": { + "node": "^18 || ^20 || >= 21" + } + }, + "node_modules/node-gyp-build": { + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz", + "integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==", + "license": "MIT", + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-to-regexp": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz", + "integrity": "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==", + "license": "MIT", + "engines": { + "node": ">=16" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.0.tgz", + "integrity": "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==", + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.6.3", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz", + "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==", + "license": "MIT", + "dependencies": { + "debug": "^4.3.5", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "mime-types": "^3.0.1", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/serve-static": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz", + "integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==", + "license": "MIT", + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/streamsearch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/type-is": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", + "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", + "license": "MIT", + "dependencies": { + "content-type": "^1.0.5", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", + "license": "MIT" + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "license": "MIT", + "engines": { + "node": ">=0.4" + } + } + } +} diff --git a/4-Sprint-Misson/package.json b/4-Sprint-Misson/package.json new file mode 100644 index 000000000..ec3afa644 --- /dev/null +++ b/4-Sprint-Misson/package.json @@ -0,0 +1,21 @@ +{ + "name": "3-sprint-misson", + "version": "1.0.0", + "type": "module", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "@prisma/client": "^6.13.0", + "bcrypt": "^6.0.0", + "dotenv": "^17.2.1", + "express": "^5.1.0", + "jsonwebtoken": "^9.0.2", + "multer": "^2.0.2" + }, + "description": "" +} diff --git a/4-Sprint-Misson/prisma/migrations/20250729043254_init_simple_blog/migration.sql b/4-Sprint-Misson/prisma/migrations/20250729043254_init_simple_blog/migration.sql new file mode 100644 index 000000000..d5c4624a3 --- /dev/null +++ b/4-Sprint-Misson/prisma/migrations/20250729043254_init_simple_blog/migration.sql @@ -0,0 +1,26 @@ +-- CreateTable +CREATE TABLE "User" ( + "id" SERIAL NOT NULL, + "nickname" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "User_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "Post" ( + "id" SERIAL NOT NULL, + "title" TEXT NOT NULL, + "content" TEXT NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + "views" INTEGER NOT NULL DEFAULT 0, + + CONSTRAINT "Post_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE UNIQUE INDEX "User_nickname_key" ON "User"("nickname"); + +-- CreateIndex +CREATE UNIQUE INDEX "Post_title_key" ON "Post"("title"); diff --git a/4-Sprint-Misson/prisma/migrations/20250801053712_init/migration.sql b/4-Sprint-Misson/prisma/migrations/20250801053712_init/migration.sql new file mode 100644 index 000000000..4bc4ab5ca --- /dev/null +++ b/4-Sprint-Misson/prisma/migrations/20250801053712_init/migration.sql @@ -0,0 +1,36 @@ +/* + Warnings: + + - You are about to drop the `Post` table. If the table is not empty, all the data it contains will be lost. + - You are about to drop the `User` table. If the table is not empty, all the data it contains will be lost. + +*/ +-- DropTable +DROP TABLE "public"."Post"; + +-- DropTable +DROP TABLE "public"."User"; + +-- CreateTable +CREATE TABLE "public"."Product" ( + "id" SERIAL NOT NULL, + "name" TEXT NOT NULL, + "description" TEXT NOT NULL, + "price" INTEGER NOT NULL, + "tags" TEXT NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "Product_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "public"."Article" ( + "id" SERIAL NOT NULL, + "title" TEXT NOT NULL, + "content" TEXT NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "Article_pkey" PRIMARY KEY ("id") +); diff --git a/4-Sprint-Misson/prisma/migrations/20250804002706_add_comment_model/migration.sql b/4-Sprint-Misson/prisma/migrations/20250804002706_add_comment_model/migration.sql new file mode 100644 index 000000000..d477f7171 --- /dev/null +++ b/4-Sprint-Misson/prisma/migrations/20250804002706_add_comment_model/migration.sql @@ -0,0 +1,10 @@ +-- CreateTable +CREATE TABLE "public"."comment" ( + "id" SERIAL NOT NULL, + "content" TEXT NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "productId" INTEGER, + "articleId" INTEGER, + + CONSTRAINT "comment_pkey" PRIMARY KEY ("id") +); diff --git a/4-Sprint-Misson/prisma/migrations/migration_lock.toml b/4-Sprint-Misson/prisma/migrations/migration_lock.toml new file mode 100644 index 000000000..044d57cdb --- /dev/null +++ b/4-Sprint-Misson/prisma/migrations/migration_lock.toml @@ -0,0 +1,3 @@ +# Please do not edit this file manually +# It should be added in your version-control system (e.g., Git) +provider = "postgresql" diff --git a/4-Sprint-Misson/prisma/schema.prisma b/4-Sprint-Misson/prisma/schema.prisma new file mode 100644 index 000000000..1645bf62a --- /dev/null +++ b/4-Sprint-Misson/prisma/schema.prisma @@ -0,0 +1,81 @@ +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") +} + +generator client { + provider = "prisma-client-js" +} + +model User { + id Int @id @default(autoincrement()) + email String @unique + nickname String + image String? + password String + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + refreshToken String? //토큰 + products Product[] + comments comment[] + articles Article[] + likes Like[] +} + + +model Product { + id Int @id @default(autoincrement()) + name String + description String + price Float + tags String? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + userId Int + user User @relation(fields: [userId], references: [id]) + comments comment[] + likes Like[] +} + +model Article { + id Int @id @default(autoincrement()) + title String + content String + userId Int + createdAt DateTime @default(now()) + updatedAt DateTime @default(now()) + + user User @relation(fields: [userId], references: [id]) + comments comment[] + likes Like[] +} + +model comment { + id Int @id @default(autoincrement()) + content String + createdAt DateTime @default(now()) + userId Int + user User @relation(fields: [userId], references: [id]) + productId Int? + product Product? @relation(fields: [productId], references: [id]) + articleId Int? + article Article? @relation(fields: [articleId], references: [id]) +} + +model Like { + id Int @id @default(autoincrement()) + userId Int + productId Int? + articleId Int? + createdAt DateTime @default(now()) + + user User @relation(fields: [userId], references: [id]) + product Product? @relation(fields: [productId], references: [id]) + article Article? @relation(fields: [articleId], references: [id]) + + @@unique([userId, productId]) + @@unique([userId, articleId]) +} + diff --git a/4-Sprint-Misson/product.js b/4-Sprint-Misson/product.js new file mode 100644 index 000000000..195814ace --- /dev/null +++ b/4-Sprint-Misson/product.js @@ -0,0 +1,148 @@ +import express from 'express'; +import { PrismaClient } from '@prisma/client'; + +const router = express.Router(); +const prisma = new PrismaClient(); + + +// 상품 등록 API +router.post('/', async (req, res) => { + const { name, description, price, tags } = req.body; + + try { + if (!name || !description || !price) { + return res.status(400).json({ error: '요소 누락 됨' }); + } + + const product = await prisma.product.create({ + data: { name, description, price, tags }, + }); + + res.status(201).json(product); + } catch (err) { + res.status(500).json({ error: '상품 등록 오류' }); + } +}); + + +// 상품 상세 조회 API +router.get('/:id', async (req, res) => { + const id = Number(req.params.id); + try { + const product = await prisma.product.findUnique({ + where: { id }, + select: { + id: true, + name: true, + description: true, + price: true, + tags: true, + createdAt: true, + }, + }); + + if (!product) { + return res.status(404).json({ error: '상품을 찾을 수 없음.' }); + } + + res.status(200).json(product); + } catch (err) { + res.status(500).json({ error: '상품 조회 중 오류' }); + } +}); + + +// 상품 수정 API +router.patch('/:id', async (req, res) => { + const id = Number(req.params.id); + const { name, description, price, tags } = req.body; + + try { + const exists = await prisma.product.findUnique({ where: { id } }); + if (!exists) return res.status(404).json({ error: '상품 없음' }); + + const product = await prisma.product.update({ + where: { id }, + data: { name, description, price, tags }, + }); + + res.status(200).json(product); + } catch (err) { + res.status(500).json({ error: '수정 중 오류' }); + } +}); + + +// 상품 삭제 API +router.delete('/:id', async (req, res) => { + const id = Number(req.params.id); + try { + const exists = await prisma.product.findUnique({ where: { id } }); + if (!exists) return res.status(404).json({ error: '상품 없음' }); + + await prisma.product.delete({ where: { id } }); + res.status(204).send(); + } catch (err) { + res.status(500).json({ error: '삭제 중 오류' }); + } +}); + + +// 상품 목록 조회 API, offsset 방식 페이지네이션, 최신순 정렬 +router.get('/', async (req, res) => { + const { + page = 1, + pageSize = 10, + sort = 'recent', + search = '', + } = req.query; + + //page와 pageSize로 offset 기반 페이지 나눔진행 + const skip = (Number(page) - 1) * Number(pageSize); + const take = Number(pageSize); + + //검색, name 또는 description에 포함되면 해당 상품 검색 + // insensitive: 대소문자 무시 + try { + const where = { + OR: [ + { name: { contains: search, mode: 'insensitive' } }, + { description: { contains: search, mode: 'insensitive' } }, + ], + }; + + const products = await prisma.product.findMany({ + where, + //최신순 정렬 + orderBy: { createdAt: sort === 'recent' ? 'desc' : 'asc' }, + skip, + take, + select: { + id: true, + name: true, + price: true, + createdAt: true, + }, + }); + + //조건에 맞는 전체 데이터 수 산출 + const total = await prisma.product.count({ where }); + + //상품 목록, pagination 내 정보 보이게 + res.status(200).json({ + data: products, + pagination: { + total, + page: Number(page), + pageSize: Number(pageSize), + totalPages: Math.ceil(total / pageSize), + }, + }); + } catch (err) { + res.status(500).json({ error: '조회 오류' }); + } +}); + + + +export default router; diff --git a/4-Sprint-Misson/src/controllers/articlesController.js b/4-Sprint-Misson/src/controllers/articlesController.js new file mode 100644 index 000000000..ab68e88fb --- /dev/null +++ b/4-Sprint-Misson/src/controllers/articlesController.js @@ -0,0 +1,140 @@ +import { PrismaClient } from "@prisma/client"; +const prisma = new PrismaClient(); + +export class ArticlesController { + // 게시글 등록 + async create(req, res) { + const { title, content } = req.body; + const userId = req.user.id; // 로그인 유저 ID + + try { + if (!title || !content) { + return res.status(400).json({ error: "제목/내용 누락" }); + } + + const article = await prisma.article.create({ + data: { title, content, userId }, + }); + + res.status(201).json(article); + } catch (err) { + res.status(500).json({ error: "게시글 등록 오류" }); + } + } + + // 게시글 상세 조회 + async getById(req, res) { + const id = Number(req.params.id); + + try { + const article = await prisma.article.findUnique({ + where: { id }, + select: { + id: true, + title: true, + content: true, + createdAt: true, + updatedAt: true, + user: { select: { id: true, email: true } }, + }, + }); + + if (!article) return res.status(404).json({ error: "게시글 없음" }); + res.status(200).json(article); + } catch (err) { + res.status(500).json({ error: "조회 오류" }); + } + } + + // 게시글 수정 + async update(req, res) { + const id = Number(req.params.id); + const { title, content } = req.body; + + try { + const article = await prisma.article.findUnique({ where: { id } }); + if (!article) return res.status(404).json({ error: "게시글 없음" }); + + if (article.userId !== req.user.id) { + return res.status(403).json({ error: "권한 없음" }); + } + + const updated = await prisma.article.update({ + where: { id }, + data: { title, content }, + }); + + res.status(200).json(updated); + } catch (err) { + res.status(500).json({ error: "수정 오류" }); + } + } + + // 게시글 삭제 + async delete(req, res) { + const id = Number(req.params.id); + + try { + const article = await prisma.article.findUnique({ where: { id } }); + if (!article) return res.status(404).json({ error: "게시글 없음" }); + + if (article.userId !== req.user.id) { + return res.status(403).json({ error: "권한 없음" }); + } + + await prisma.article.delete({ where: { id } }); + res.status(204).send(); + } catch (err) { + res.status(500).json({ error: "삭제 오류" }); + } + } + + // 게시글 목록 조회 (최신순, 검색 포함) + async list(req, res) { + const { + page = 1, + pageSize = 10, + sort = "recent", + search = "", + } = req.query; + + const skip = (Number(page) - 1) * Number(pageSize); + const take = Number(pageSize); + + try { + const where = { + OR: [ + { title: { contains: search, mode: "insensitive" } }, + { content: { contains: search, mode: "insensitive" } }, + ], + }; + + const articles = await prisma.article.findMany({ + where, + orderBy: { createdAt: sort === "recent" ? "desc" : "asc" }, + skip, + take, + select: { + id: true, + title: true, + createdAt: true, + user: { select: { id: true, email: true } }, + }, + }); + + const total = await prisma.article.count({ where }); + + res.status(200).json({ + data: articles, + pagination: { + total, + page: Number(page), + pageSize: Number(pageSize), + totalPages: Math.ceil(total / pageSize), + }, + }); + } catch (err) { + res.status(500).json({ error: "목록 조회 오류" }); + } + } +} \ No newline at end of file diff --git a/4-Sprint-Misson/src/controllers/authController.js b/4-Sprint-Misson/src/controllers/authController.js new file mode 100644 index 000000000..66a4b8eb0 --- /dev/null +++ b/4-Sprint-Misson/src/controllers/authController.js @@ -0,0 +1,77 @@ +import { PrismaClient } from "@prisma/client"; +import bcrypt from "bcrypt"; +import { + generateAccessToken, + generateRefreshToken, + verifyRefreshToken, +} from "../utils/jwt.js"; + +const prisma = new PrismaClient(); + +export class AuthController { + // 로그인 + async login(req, res) { + const { email, password } = req.body; + try { + const user = await prisma.user.findUnique({ where: { email } }); + if (!user) return res.status(404).json({ error: "존재하지 않는 이메일" }); + + const isMatch = await bcrypt.compare(password, user.password); + if (!isMatch) return res.status(401).json({ error: "비밀번호 불일치" }); + + const accessToken = generateAccessToken(user); + const refreshToken = generateRefreshToken(user); + + // DB에 refresh token 저장 + await prisma.user.update({ + where: { id: user.id }, + data: { refreshToken }, + }); + + res.json({ accessToken, refreshToken }); + } catch (err) { + res.status(500).json({ error: "로그인 실패" }); + } + } + + // 토큰 갱신 + async refresh(req, res) { + const { refreshToken } = req.body; + if (!refreshToken) return res.status(400).json({ error: "Refresh Token 필요" }); + + try { + const payload = verifyRefreshToken(refreshToken); + + const user = await prisma.user.findUnique({ where: { id: payload.id } }); + if (!user || user.refreshToken !== refreshToken) { + return res.status(403).json({ error: "Refresh Token 불일치" }); + } + + const newAccessToken = generateAccessToken(user); + const newRefreshToken = generateRefreshToken(user); + + // 새로운 refresh token 갱신 + await prisma.user.update({ + where: { id: user.id }, + data: { refreshToken: newRefreshToken }, + }); + + res.json({ accessToken: newAccessToken, refreshToken: newRefreshToken }); + } catch (err) { + res.status(403).json({ error: "Refresh Token 검증 실패" }); + } + } + + // 로그아웃 + async logout(req, res) { + try { + await prisma.user.update({ + where: { id: req.user.id }, + data: { refreshToken: null }, + }); + res.json({ message: "로그아웃 완료" }); + } catch (err) { + res.status(500).json({ error: "로그아웃 실패" }); + } + } +} diff --git a/4-Sprint-Misson/src/controllers/commentsController.js b/4-Sprint-Misson/src/controllers/commentsController.js new file mode 100644 index 000000000..084d1425d --- /dev/null +++ b/4-Sprint-Misson/src/controllers/commentsController.js @@ -0,0 +1,133 @@ +import { PrismaClient } from "@prisma/client"; +const prisma = new PrismaClient(); + +export class CommentsController { + // 상품 댓글 등록 + async createForProduct(req, res) { + const { productId } = req.params; + const { content } = req.body; + const userId = req.user.id; + + try { + const comment = await prisma.comment.create({ + data: { content, productId: Number(productId), userId }, + }); + res.status(201).json(comment); + } catch (err) { + res.status(500).json({ error: "상품 댓글 등록 실패" }); + } + } + + // 게시글 댓글 등록 + async createForArticle(req, res) { + const { articleId } = req.params; + const { content } = req.body; + const userId = req.user.id; + + try { + const comment = await prisma.comment.create({ + data: { content, articleId: Number(articleId), userId }, + }); + res.status(201).json(comment); + } catch (err) { + res.status(500).json({ error: "게시글 댓글 등록 실패" }); + } + } + + // 댓글 수정 + async update(req, res) { + const { id } = req.params; + const { content } = req.body; + const userId = req.user.id; + + try { + const comment = await prisma.comment.findUnique({ where: { id: Number(id) } }); + if (!comment) return res.status(404).json({ error: "댓글 없음" }); + + if (comment.userId !== userId) { + return res.status(403).json({ error: "권한 없음" }); + } + + const updated = await prisma.comment.update({ + where: { id: Number(id) }, + data: { content }, + }); + + res.json(updated); + } catch (err) { + res.status(500).json({ error: "댓글 수정 실패" }); + } + } + + // 댓글 삭제 + async delete(req, res) { + const { id } = req.params; + const userId = req.user.id; + + try { + const comment = await prisma.comment.findUnique({ where: { id: Number(id) } }); + if (!comment) return res.status(404).json({ error: "댓글 없음" }); + + if (comment.userId !== userId) { + return res.status(403).json({ error: "권한 없음" }); + } + + await prisma.comment.delete({ where: { id: Number(id) } }); + res.json({ message: "삭제 완료" }); + } catch (err) { + res.status(500).json({ error: "댓글 삭제 실패" }); + } + } + + // 상품 댓글 목록 조회 + async listForProduct(req, res) { + const { productId } = req.params; + const { cursor, take = 10 } = req.query; + + try { + const comments = await prisma.comment.findMany({ + where: { productId: Number(productId) }, + take: Number(take), + skip: cursor ? 1 : 0, + cursor: cursor ? { id: Number(cursor) } : undefined, + orderBy: { id: "asc" }, + select: { + id: true, + content: true, + createdAt: true, + user: { select: { id: true, nickname: true } }, + }, + }); + + res.json(comments); + } catch (err) { + res.status(500).json({ error: "조회 실패" }); + } + } + + // 게시글 댓글 목록 조회 + async listForArticle(req, res) { + const { articleId } = req.params; + const { cursor, take = 10 } = req.query; + + try { + const comments = await prisma.comment.findMany({ + where: { articleId: Number(articleId) }, + take: Number(take), + skip: cursor ? 1 : 0, + cursor: cursor ? { id: Number(cursor) } : undefined, + orderBy: { id: "asc" }, + select: { + id: true, + content: true, + createdAt: true, + user: { select: { id: true, nickname: true } }, + }, + }); + + res.json(comments); + } catch (err) { + res.status(500).json({ error: "조회 실패" }); + } + } +} diff --git a/4-Sprint-Misson/src/controllers/likeController.js b/4-Sprint-Misson/src/controllers/likeController.js new file mode 100644 index 000000000..de9a0e259 --- /dev/null +++ b/4-Sprint-Misson/src/controllers/likeController.js @@ -0,0 +1,70 @@ +import { PrismaClient } from "@prisma/client"; +const prisma = new PrismaClient(); + +export class LikeController { + // 상품 좋아요 / 취소 + async toggleProductLike(req, res) { + const userId = req.user.id; + const productId = Number(req.params.productId); + + try { + const existing = await prisma.like.findUnique({ + where: { userId_productId: { userId, productId } }, + }); + + if (existing) { + // 좋아요 취소 + await prisma.like.delete({ where: { id: existing.id } }); + return res.json({ message: "상품 좋아요 취소" }); + } else { + // 좋아요 등록 + await prisma.like.create({ + data: { userId, productId }, + }); + return res.json({ message: "상품 좋아요 등록" }); + } + } catch (err) { + res.status(500).json({ error: "상품 좋아요 처리 실패" }); + } + } + + // 게시글 좋아요 / 취소 + async toggleArticleLike(req, res) { + const userId = req.user.id; + const articleId = Number(req.params.articleId); + + try { + const existing = await prisma.like.findUnique({ + where: { userId_articleId: { userId, articleId } }, + }); + + if (existing) { + await prisma.like.delete({ where: { id: existing.id } }); + return res.json({ message: "게시글 좋아요 취소" }); + } else { + await prisma.like.create({ + data: { userId, articleId }, + }); + return res.json({ message: "게시글 좋아요 등록" }); + } + } catch (err) { + res.status(500).json({ error: "게시글 좋아요 처리 실패" }); + } + } + + // 유저가 좋아요 누른 상품 목록 조회 + async getUserLikedProducts(req, res) { + const userId = req.user.id; + try { + const likes = await prisma.like.findMany({ + where: { userId, productId: { not: null } }, + include: { product: true }, + }); + + const products = likes.map((like) => like.product); + res.json(products); + } catch (err) { + res.status(500).json({ error: "좋아요 상품 조회 실패" }); + } + } +} diff --git a/4-Sprint-Misson/src/controllers/productController.js b/4-Sprint-Misson/src/controllers/productController.js new file mode 100644 index 000000000..d06e5dc53 --- /dev/null +++ b/4-Sprint-Misson/src/controllers/productController.js @@ -0,0 +1,132 @@ +import { PrismaClient } from '@prisma/client'; + +const prisma = new PrismaClient(); + +// 상품 등록 +export const createProduct = async (req, res) => { + const { name, description, price, tags } = req.body; + + try { + if (!name || !description || !price) { + return res.status(400).json({ error: '요소 누락 됨' }); + } + + const product = await prisma.product.create({ + data: { name, description, price, tags }, + }); + + res.status(201).json(product); + } catch (err) { + res.status(500).json({ error: '상품 등록 오류' }); + } +}; + +// 상품 상세 조회 +export const getProductById = async (req, res) => { + const productId = Number(req.params.id); + const userId = req.user?.id; + + try { + const product = await prisma.product.findUnique({ + where: { id: productId }, + }); + + if (!product) return res.status(404).json({ error: "상품 없음" }); + + let isLiked = false; + if (userId) { + const like = await prisma.like.findUnique({ + where: { userId_productId: { userId, productId } }, + }); + isLiked = !!like; + } + + res.json({ ...product, isLiked }); + } catch (err) { + res.status(500).json({ error: "상품 조회 실패" }); + } +}; + +// 상품 수정 +export const updateProduct = async (req, res) => { + const id = Number(req.params.id); + const { name, description, price, tags } = req.body; + + try { + const exists = await prisma.product.findUnique({ where: { id } }); + if (!exists) return res.status(404).json({ error: '상품 없음' }); + + const product = await prisma.product.update({ + where: { id }, + data: { name, description, price, tags }, + }); + + res.status(200).json(product); + } catch (err) { + res.status(500).json({ error: '수정 중 오류' }); + } +}; + +// 상품 삭제 +export const deleteProduct = async (req, res) => { + const id = Number(req.params.id); + + try { + const exists = await prisma.product.findUnique({ where: { id } }); + if (!exists) return res.status(404).json({ error: '상품 없음' }); + + await prisma.product.delete({ where: { id } }); + res.status(204).send(); + } catch (err) { + res.status(500).json({ error: '삭제 중 오류' }); + } +}; + +// 상품 목록 조회 (offset 기반 페이지네이션, 최신순 정렬) +export const getProducts = async (req, res) => { + const { + page = 1, + pageSize = 10, + sort = 'recent', + search = '', + } = req.query; + + const skip = (Number(page) - 1) * Number(pageSize); + const take = Number(pageSize); + + try { + const where = { + OR: [ + { name: { contains: search, mode: 'insensitive' } }, + { description: { contains: search, mode: 'insensitive' } }, + ], + }; + + const products = await prisma.product.findMany({ + where, + orderBy: { createdAt: sort === 'recent' ? 'desc' : 'asc' }, + skip, + take, + select: { + id: true, + name: true, + price: true, + createdAt: true, + }, + }); + + const total = await prisma.product.count({ where }); + + res.status(200).json({ + data: products, + pagination: { + total, + page: Number(page), + pageSize: Number(pageSize), + totalPages: Math.ceil(total / pageSize), + }, + }); + } catch (err) { + res.status(500).json({ error: '조회 오류' }); + } +}; diff --git a/4-Sprint-Misson/src/controllers/usersController.js b/4-Sprint-Misson/src/controllers/usersController.js new file mode 100644 index 000000000..2f4a4158b --- /dev/null +++ b/4-Sprint-Misson/src/controllers/usersController.js @@ -0,0 +1,108 @@ +import { PrismaClient } from "@prisma/client"; +import bcrypt from "bcrypt"; + +const prisma = new PrismaClient(); + +export class UsersController { + // 내 정보 조회 + async getProfile(req, res) { + try { + const user = await prisma.user.findUnique({ + where: { id: req.user.id }, + select: { + id: true, + email: true, + nickname: true, + image: true, + createdAt: true, + updatedAt: true, + }, + }); + res.json(user); + } catch (err) { + res.status(500).json({ error: "내 정보 조회 실패" }); + } + } + + // 내 정보 수정 + async updateProfile(req, res) { + const { nickname, image } = req.body; + try { + const updated = await prisma.user.update({ + where: { id: req.user.id }, + data: { nickname, image }, + select: { + id: true, + email: true, + nickname: true, + image: true, + createdAt: true, + updatedAt: true, + }, + }); + res.json(updated); + } catch (err) { + res.status(500).json({ error: "내 정보 수정 실패" }); + } + } + + // 비밀번호 변경 + async changePassword(req, res) { + const { oldPassword, newPassword } = req.body; + try { + const user = await prisma.user.findUnique({ where: { id: req.user.id } }); + if (!user) return res.status(404).json({ error: "유저 없음" }); + + const isMatch = await bcrypt.compare(oldPassword, user.password); + if (!isMatch) return res.status(400).json({ error: "기존 비밀번호 불일치" }); + + const hashed = await bcrypt.hash(newPassword, 10); + await prisma.user.update({ + where: { id: req.user.id }, + data: { password: hashed }, + }); + + res.json({ message: "비밀번호 변경 완료" }); + } catch (err) { + res.status(500).json({ error: "비밀번호 변경 실패" }); + } + } + + // 내가 등록한 상품 목록 + async getMyProducts(req, res) { + const { page = 1, pageSize = 10 } = req.query; + const skip = (Number(page) - 1) * Number(pageSize); + const take = Number(pageSize); + + try { + const products = await prisma.product.findMany({ + where: { userId: req.user.id }, + skip, + take, + orderBy: { createdAt: "desc" }, + select: { + id: true, + name: true, + description: true, + price: true, + tags: true, + createdAt: true, + }, + }); + + const total = await prisma.product.count({ where: { userId: req.user.id } }); + + res.json({ + data: products, + pagination: { + total, + page: Number(page), + pageSize: Number(pageSize), + totalPages: Math.ceil(total / pageSize), + }, + }); + } catch (err) { + res.status(500).json({ error: "내 상품 목록 조회 실패" }); + } + } +} diff --git a/4-Sprint-Misson/src/middlewares/auth.js b/4-Sprint-Misson/src/middlewares/auth.js new file mode 100644 index 000000000..4373ab18d --- /dev/null +++ b/4-Sprint-Misson/src/middlewares/auth.js @@ -0,0 +1,14 @@ +import jwt from "jsonwebtoken"; + +export const authenticate = (req, res, next) => { + const token = req.headers.authorization?.split(" ")[1]; + if (!token) return res.status(401).json({ error: "인증 필요" }); + + try { + const decoded = jwt.verify(token, process.env.JWT_SECRET); + req.user = decoded; + next(); + } catch (err) { + res.status(401).json({ error: "유효하지 않은 토큰" }); + } +}; \ No newline at end of file diff --git a/4-Sprint-Misson/src/routes/articleRoutes.js b/4-Sprint-Misson/src/routes/articleRoutes.js new file mode 100644 index 000000000..4e3ec7e3a --- /dev/null +++ b/4-Sprint-Misson/src/routes/articleRoutes.js @@ -0,0 +1,17 @@ +import express from "express"; +import { ArticlesController } from "../controllers/articlesController.js"; +import { authenticate } from "../middlewares/auth.js"; + +const router = express.Router(); +const controller = new ArticlesController(); + +//인증 +router.post("/", authenticate, controller.create.bind(controller)); +router.patch("/:id", authenticate, controller.update.bind(controller)); +router.delete("/:id", authenticate, controller.delete.bind(controller)); + +//인증 없음 +router.get("/", controller.list.bind(controller)); +router.get("/:id", controller.getById.bind(controller)); + +export default router; \ No newline at end of file diff --git a/4-Sprint-Misson/src/routes/authRoutes.js b/4-Sprint-Misson/src/routes/authRoutes.js new file mode 100644 index 000000000..6a6092cb5 --- /dev/null +++ b/4-Sprint-Misson/src/routes/authRoutes.js @@ -0,0 +1,17 @@ +import express from "express"; +import { AuthController } from "../controllers/authController.js"; +import { authenticate } from "../middlewares/auth.js"; + +const router = express.Router(); +const controller = new AuthController(); + +// 로그인 +router.post("/login", controller.login.bind(controller)); + +// 토큰 갱신 +router.post("/refresh", controller.refresh.bind(controller)); + +// 로그아웃 - Access Token 필요 +router.post("/logout", authenticate, controller.logout.bind(controller)); + +export default router; diff --git a/4-Sprint-Misson/src/routes/commentRoutes.js b/4-Sprint-Misson/src/routes/commentRoutes.js new file mode 100644 index 000000000..bd6d716ab --- /dev/null +++ b/4-Sprint-Misson/src/routes/commentRoutes.js @@ -0,0 +1,20 @@ +import express from "express"; +import { CommentsController } from "../controllers/commentsController.js"; +import { authenticate } from "../middlewares/auth.js"; + +const router = express.Router(); +const controller = new CommentsController(); + +// 등록- 로그인 필요 +router.post("/product/:productId", authenticate, controller.createForProduct.bind(controller)); +router.post("/article/:articleId", authenticate, controller.createForArticle.bind(controller)); + +// 수정/삭제 - 작성한 사람만 가능 +router.patch("/:id", authenticate, controller.update.bind(controller)); +router.delete("/:id", authenticate, controller.delete.bind(controller)); + +// 조회 - 인증 X +router.get("/product/:productId", controller.listForProduct.bind(controller)); +router.get("/article/:articleId", controller.listForArticle.bind(controller)); + +export default router; diff --git a/4-Sprint-Misson/src/routes/likeRoutes.js b/4-Sprint-Misson/src/routes/likeRoutes.js new file mode 100644 index 000000000..943a8c723 --- /dev/null +++ b/4-Sprint-Misson/src/routes/likeRoutes.js @@ -0,0 +1,17 @@ +import express from "express"; +import { LikeController } from "../controllers/likeController.js"; +import { authenticate } from "../middlewares/auth.js"; + +const router = express.Router(); +const controller = new LikeController(); + +// 상품 좋아요 +router.post("/products/:productId/like", authenticate, controller.toggleProductLike.bind(controller)); + +// 게시글 좋아요 +router.post("/articles/:articleId/like", authenticate, controller.toggleArticleLike.bind(controller)); + +// 좋아요한 상품 목록 조회 +router.get("/me/products", authenticate, controller.getUserLikedProducts.bind(controller)); + +export default router; diff --git a/4-Sprint-Misson/src/routes/productRoutes.js b/4-Sprint-Misson/src/routes/productRoutes.js new file mode 100644 index 000000000..7971d71c5 --- /dev/null +++ b/4-Sprint-Misson/src/routes/productRoutes.js @@ -0,0 +1,19 @@ +import express from 'express'; +import { + createProduct, + getProductById, + updateProduct, + deleteProduct, + getProducts, +} from '../controllers/productController.js'; +import { authenticate } from '../middlewares/auth.js'; + +const router = express.Router(); + +router.post('/', authenticate, createProduct); // 등록 +router.get('/:id', getProductById); // 상세 조회 +router.patch('/:id', authenticate, updateProduct); // 수정 +router.delete('/:id', authenticate, deleteProduct); // 삭제 +router.get('/', getProducts); // 목록 조회 + +export default router; diff --git a/4-Sprint-Misson/src/routes/usersRoutes.js b/4-Sprint-Misson/src/routes/usersRoutes.js new file mode 100644 index 000000000..20d2b2ae0 --- /dev/null +++ b/4-Sprint-Misson/src/routes/usersRoutes.js @@ -0,0 +1,18 @@ +import express from "express"; +import { UsersController } from "../controllers/usersController.js"; +import { authenticate } from "../middlewares/auth.js"; + +const router = express.Router(); +const controller = new UsersController(); + +// 내 정보 조회 / 수정 +router.get("/me", authenticate, controller.getProfile.bind(controller)); +router.patch("/me", authenticate, controller.updateProfile.bind(controller)); + +// 비밀번호 변경 +router.patch("/me/password", authenticate, controller.changePassword.bind(controller)); + +// 내가 등록한 상품 목록 +router.get("/me/products", authenticate, controller.getMyProducts.bind(controller)); + +export default router; diff --git a/4-Sprint-Misson/src/server.js b/4-Sprint-Misson/src/server.js new file mode 100644 index 000000000..c44a31f68 --- /dev/null +++ b/4-Sprint-Misson/src/server.js @@ -0,0 +1,22 @@ +import express from "express"; +import dotenv from "dotenv"; + +import authRoutes from "./routes/authRoutes.js"; +import articleRoutes from "./routes/articleRoutes.js"; +import commentRoutes from "./routes/commentRoutes.js"; +import productRoutes from "./routes/productRoutes.js"; + + +dotenv.config(); +const app = express(); + +app.use(express.json()); + +app.use("/auth", authRoutes); +app.use("/articles", articleRoutes); +app.use("/comments", commentRoutes); +app.use("/products", productRoutes); + +app.listen(3000, () => { + console.log("Server running on http://localhost:3000"); +}); diff --git a/4-Sprint-Misson/src/uploads/1753935463073-536058091.png b/4-Sprint-Misson/src/uploads/1753935463073-536058091.png new file mode 100644 index 0000000000000000000000000000000000000000..fb52717db5b466ad749b5aa4f14fd314553291b3 GIT binary patch literal 84440 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 Z{vT>UqZv`+i39)u002ovPDHLkV1kJwN^}4K literal 0 HcmV?d00001 diff --git a/4-Sprint-Misson/src/uploads/1753935773083-773169808.jpg b/4-Sprint-Misson/src/uploads/1753935773083-773169808.jpg new file mode 100644 index 0000000000000000000000000000000000000000..4c2f8984f9a662f5d2895584cf3265d619a1f00d GIT binary patch literal 7016 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 Date: Mon, 22 Sep 2025 19:39:47 +0900 Subject: [PATCH 4/5] Add files via upload --- 5-Sprint-Misson/package-lock.json | 2351 +++++++++++++++++ 5-Sprint-Misson/package.json | 35 + .../migration.sql | 26 + .../20250801053712_init/migration.sql | 36 + .../migration.sql | 10 + .../migration.sql | 79 + .../prisma/migrations/migration_lock.toml | 3 + 5-Sprint-Misson/prisma/schema.prisma | 81 + 5-Sprint-Misson/product.js | 148 ++ .../src/controllers/articleController.ts | 67 + .../src/controllers/authController.ts | 91 + .../src/controllers/commentsController.ts | 74 + .../src/controllers/likeController.ts | 35 + .../src/controllers/productController.ts | 95 + .../src/controllers/usersController.ts | 50 + 5-Sprint-Misson/src/dtos/article.dto.ts | 12 + 5-Sprint-Misson/src/dtos/comment.dto.ts | 7 + 5-Sprint-Misson/src/dtos/like.dto.ts | 3 + 5-Sprint-Misson/src/dtos/product.dto.ts | 34 + 5-Sprint-Misson/src/dtos/user.dto.ts | 17 + 5-Sprint-Misson/src/index.js | 49 + 5-Sprint-Misson/src/middlewares/auth.ts | 25 + .../src/repositories/articlesRepository.ts | 62 + 5-Sprint-Misson/src/routes/articleRoutes.ts | 14 + 5-Sprint-Misson/src/routes/authRoutes.ts | 17 + 5-Sprint-Misson/src/routes/commentRoutes.ts | 20 + 5-Sprint-Misson/src/routes/likeRoutes.ts | 17 + 5-Sprint-Misson/src/routes/productRoutes.ts | 19 + 5-Sprint-Misson/src/routes/usersRoutes.ts | 18 + 5-Sprint-Misson/src/server.ts | 24 + .../src/services/articlesService.ts | 45 + .../src/services/commentsService.ts | 55 + 5-Sprint-Misson/src/services/likeService.ts | 45 + .../src/services/productService.ts | 80 + 5-Sprint-Misson/src/services/userService.ts | 73 + 5-Sprint-Misson/src/types/express/index.d.ts | 9 + .../src/uploads/1753935463073-536058091.png | Bin 0 -> 84440 bytes .../src/uploads/1753935773083-773169808.jpg | Bin 0 -> 7016 bytes 5-Sprint-Misson/src/utils/jwt.ts | 35 + 5-Sprint-Misson/src/utils/prisma.ts | 3 + 5-Sprint-Misson/tsconfig.json | 20 + 41 files changed, 3884 insertions(+) create mode 100644 5-Sprint-Misson/package-lock.json create mode 100644 5-Sprint-Misson/package.json create mode 100644 5-Sprint-Misson/prisma/migrations/20250729043254_init_simple_blog/migration.sql create mode 100644 5-Sprint-Misson/prisma/migrations/20250801053712_init/migration.sql create mode 100644 5-Sprint-Misson/prisma/migrations/20250804002706_add_comment_model/migration.sql create mode 100644 5-Sprint-Misson/prisma/migrations/20250917020714_add_tags_array/migration.sql create mode 100644 5-Sprint-Misson/prisma/migrations/migration_lock.toml create mode 100644 5-Sprint-Misson/prisma/schema.prisma create mode 100644 5-Sprint-Misson/product.js create mode 100644 5-Sprint-Misson/src/controllers/articleController.ts create mode 100644 5-Sprint-Misson/src/controllers/authController.ts create mode 100644 5-Sprint-Misson/src/controllers/commentsController.ts create mode 100644 5-Sprint-Misson/src/controllers/likeController.ts create mode 100644 5-Sprint-Misson/src/controllers/productController.ts create mode 100644 5-Sprint-Misson/src/controllers/usersController.ts create mode 100644 5-Sprint-Misson/src/dtos/article.dto.ts create mode 100644 5-Sprint-Misson/src/dtos/comment.dto.ts create mode 100644 5-Sprint-Misson/src/dtos/like.dto.ts create mode 100644 5-Sprint-Misson/src/dtos/product.dto.ts create mode 100644 5-Sprint-Misson/src/dtos/user.dto.ts create mode 100644 5-Sprint-Misson/src/index.js create mode 100644 5-Sprint-Misson/src/middlewares/auth.ts create mode 100644 5-Sprint-Misson/src/repositories/articlesRepository.ts create mode 100644 5-Sprint-Misson/src/routes/articleRoutes.ts create mode 100644 5-Sprint-Misson/src/routes/authRoutes.ts create mode 100644 5-Sprint-Misson/src/routes/commentRoutes.ts create mode 100644 5-Sprint-Misson/src/routes/likeRoutes.ts create mode 100644 5-Sprint-Misson/src/routes/productRoutes.ts create mode 100644 5-Sprint-Misson/src/routes/usersRoutes.ts create mode 100644 5-Sprint-Misson/src/server.ts create mode 100644 5-Sprint-Misson/src/services/articlesService.ts create mode 100644 5-Sprint-Misson/src/services/commentsService.ts create mode 100644 5-Sprint-Misson/src/services/likeService.ts create mode 100644 5-Sprint-Misson/src/services/productService.ts create mode 100644 5-Sprint-Misson/src/services/userService.ts create mode 100644 5-Sprint-Misson/src/types/express/index.d.ts create mode 100644 5-Sprint-Misson/src/uploads/1753935463073-536058091.png create mode 100644 5-Sprint-Misson/src/uploads/1753935773083-773169808.jpg create mode 100644 5-Sprint-Misson/src/utils/jwt.ts create mode 100644 5-Sprint-Misson/src/utils/prisma.ts create mode 100644 5-Sprint-Misson/tsconfig.json diff --git a/5-Sprint-Misson/package-lock.json b/5-Sprint-Misson/package-lock.json new file mode 100644 index 000000000..b0e85803d --- /dev/null +++ b/5-Sprint-Misson/package-lock.json @@ -0,0 +1,2351 @@ +{ + "name": "5-sprint-misson", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "5-sprint-misson", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "@prisma/client": "^6.13.0", + "bcrypt": "^6.0.0", + "class-transformer": "^0.5.1", + "class-validator": "^0.14.2", + "dotenv": "^17.2.1", + "express": "^5.1.0", + "jsonwebtoken": "^9.0.2", + "multer": "^2.0.2", + "reflect-metadata": "^0.2.2" + }, + "devDependencies": { + "@types/bcrypt": "^6.0.0", + "@types/express": "^5.0.3", + "@types/jsonwebtoken": "^9.0.10", + "@types/node": "^24.5.0", + "nodemon": "^3.1.10", + "ts-node": "^10.9.2", + "typescript": "^5.9.2" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@prisma/client": { + "version": "6.16.1", + "resolved": "https://registry.npmjs.org/@prisma/client/-/client-6.16.1.tgz", + "integrity": "sha512-QaBCOY29lLAxEFFJgBPyW3WInCW52fJeQTmWx/h6YsP5u0bwuqP51aP0uhqFvhK9DaZPwvai/M4tSDYLVE9vRg==", + "hasInstallScript": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "peerDependencies": { + "prisma": "*", + "typescript": ">=5.1.0" + }, + "peerDependenciesMeta": { + "prisma": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/@prisma/config": { + "version": "6.16.1", + "resolved": "https://registry.npmjs.org/@prisma/config/-/config-6.16.1.tgz", + "integrity": "sha512-sz3uxRPNL62QrJ0EYiujCFkIGZ3hg+9hgC1Ae1HjoYuj0BxCqHua4JNijYvYCrh9LlofZDZcRBX3tHBfLvAngA==", + "license": "Apache-2.0", + "optional": true, + "peer": true, + "dependencies": { + "c12": "3.1.0", + "deepmerge-ts": "7.1.5", + "effect": "3.16.12", + "empathic": "2.0.0" + } + }, + "node_modules/@prisma/debug": { + "version": "6.16.1", + "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-6.16.1.tgz", + "integrity": "sha512-RWv/VisW5vJE4cDRTuAHeVedtGoItXTnhuLHsSlJ9202QKz60uiXWywBlVcqXVq8bFeIZoCoWH+R1duZJPwqLw==", + "license": "Apache-2.0", + "optional": true, + "peer": true + }, + "node_modules/@prisma/engines": { + "version": "6.16.1", + "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-6.16.1.tgz", + "integrity": "sha512-EOnEM5HlosPudBqbI+jipmaW/vQEaF0bKBo4gVkGabasINHR6RpC6h44fKZEqx4GD8CvH+einD2+b49DQrwrAg==", + "hasInstallScript": true, + "license": "Apache-2.0", + "optional": true, + "peer": true, + "dependencies": { + "@prisma/debug": "6.16.1", + "@prisma/engines-version": "6.16.0-7.1c57fdcd7e44b29b9313256c76699e91c3ac3c43", + "@prisma/fetch-engine": "6.16.1", + "@prisma/get-platform": "6.16.1" + } + }, + "node_modules/@prisma/engines-version": { + "version": "6.16.0-7.1c57fdcd7e44b29b9313256c76699e91c3ac3c43", + "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-6.16.0-7.1c57fdcd7e44b29b9313256c76699e91c3ac3c43.tgz", + "integrity": "sha512-ThvlDaKIVrnrv97ujNFDYiQbeMQpLa0O86HFA2mNoip4mtFqM7U5GSz2ie1i2xByZtvPztJlNRgPsXGeM/kqAA==", + "license": "Apache-2.0", + "optional": true, + "peer": true + }, + "node_modules/@prisma/fetch-engine": { + "version": "6.16.1", + "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-6.16.1.tgz", + "integrity": "sha512-fl/PKQ8da5YTayw86WD3O9OmKJEM43gD3vANy2hS5S1CnfW2oPXk+Q03+gUWqcKK306QqhjjIHRFuTZ31WaosQ==", + "license": "Apache-2.0", + "optional": true, + "peer": true, + "dependencies": { + "@prisma/debug": "6.16.1", + "@prisma/engines-version": "6.16.0-7.1c57fdcd7e44b29b9313256c76699e91c3ac3c43", + "@prisma/get-platform": "6.16.1" + } + }, + "node_modules/@prisma/get-platform": { + "version": "6.16.1", + "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-6.16.1.tgz", + "integrity": "sha512-kUfg4vagBG7dnaGRcGd1c0ytQFcDj2SUABiuveIpL3bthFdTLI6PJeLEia6Q8Dgh+WhPdo0N2q0Fzjk63XTyaA==", + "license": "Apache-2.0", + "optional": true, + "peer": true, + "dependencies": { + "@prisma/debug": "6.16.1" + } + }, + "node_modules/@standard-schema/spec": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.0.0.tgz", + "integrity": "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==", + "license": "MIT", + "optional": true, + "peer": true + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", + "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/bcrypt": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@types/bcrypt/-/bcrypt-6.0.0.tgz", + "integrity": "sha512-/oJGukuH3D2+D+3H4JWLaAsJ/ji86dhRidzZ/Od7H/i8g+aCmvkeCc6Ni/f9uxGLSQVCRZkX2/lqEFG2BvWtlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/body-parser": { + "version": "1.19.6", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", + "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/express": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.3.tgz", + "integrity": "sha512-wGA0NX93b19/dZC1J18tKWVIYWyyF2ZjT9vin/NRu0qzzvfVzWjs04iq2rQ3H65vCTQYlRqs3YHfY7zjdV+9Kw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^5.0.0", + "@types/serve-static": "*" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.0.7.tgz", + "integrity": "sha512-R+33OsgWw7rOhD1emjU7dzCDHucJrgJXMA5PYCzJxVil0dsyx5iBEPHqpPfiKNJQb7lZ1vxwoLR4Z87bBUpeGQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/http-errors": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", + "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/jsonwebtoken": { + "version": "9.0.10", + "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.10.tgz", + "integrity": "sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/ms": "*", + "@types/node": "*" + } + }, + "node_modules/@types/mime": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "24.5.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.5.0.tgz", + "integrity": "sha512-y1dMvuvJspJiPSDZUQ+WMBvF7dpnEqN4x9DDC9ie5Fs/HUZJA3wFp7EhHoVaKX/iI0cRoECV8X2jL8zi0xrHCg==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.12.0" + } + }, + "node_modules/@types/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/send": { + "version": "0.17.5", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.5.tgz", + "integrity": "sha512-z6F2D3cOStZvuk2SaP6YrwkNO65iTZcwA2ZkSABegdkAh/lf+Aa/YQndZVfmEXT5vgAp6zv06VQ3ejSVjAny4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.15.8", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.8.tgz", + "integrity": "sha512-roei0UY3LhpOJvjbIP6ZZFngyLKl5dskOtDhxY5THRSpO+ZI+nzJ+m5yUMzGrp89YRa7lvknKkMYjqQFGwA7Sg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*", + "@types/send": "*" + } + }, + "node_modules/@types/validator": { + "version": "13.15.3", + "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.15.3.tgz", + "integrity": "sha512-7bcUmDyS6PN3EuD9SlGGOxM77F8WLVsrwkxyWxKnxzmXoequ6c7741QBrANq6htVRGOITJ7z72mTP6Z4XyuG+Q==", + "license": "MIT" + }, + "node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "license": "MIT", + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/append-field": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", + "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==", + "license": "MIT" + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true, + "license": "MIT" + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/bcrypt": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-6.0.0.tgz", + "integrity": "sha512-cU8v/EGSrnH+HnxV2z0J7/blxH8gq7Xh2JFT6Aroax7UohdmiJJlxApMxtKfuI7z68NvvVcmR78k2LbT6efhRg==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "node-addon-api": "^8.3.0", + "node-gyp-build": "^4.8.4" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/body-parser": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz", + "integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==", + "license": "MIT", + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^1.0.5", + "debug": "^4.4.0", + "http-errors": "^2.0.0", + "iconv-lite": "^0.6.3", + "on-finished": "^2.4.1", + "qs": "^6.14.0", + "raw-body": "^3.0.0", + "type-is": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "license": "BSD-3-Clause" + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "license": "MIT" + }, + "node_modules/busboy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "dependencies": { + "streamsearch": "^1.1.0" + }, + "engines": { + "node": ">=10.16.0" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/c12": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/c12/-/c12-3.1.0.tgz", + "integrity": "sha512-uWoS8OU1MEIsOv8p/5a82c3H31LsWVR5qiyXVfBNOzfffjUWtPnhAb4BYI2uG2HfGmZmFjCtui5XNWaps+iFuw==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "chokidar": "^4.0.3", + "confbox": "^0.2.2", + "defu": "^6.1.4", + "dotenv": "^16.6.1", + "exsolve": "^1.0.7", + "giget": "^2.0.0", + "jiti": "^2.4.2", + "ohash": "^2.0.11", + "pathe": "^2.0.3", + "perfect-debounce": "^1.0.0", + "pkg-types": "^2.2.0", + "rc9": "^2.1.2" + }, + "peerDependencies": { + "magicast": "^0.3.5" + }, + "peerDependenciesMeta": { + "magicast": { + "optional": true + } + } + }, + "node_modules/c12/node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/c12/node_modules/dotenv": { + "version": "16.6.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", + "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", + "license": "BSD-2-Clause", + "optional": true, + "peer": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/c12/node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/citty": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/citty/-/citty-0.1.6.tgz", + "integrity": "sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "consola": "^3.2.3" + } + }, + "node_modules/class-transformer": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.5.1.tgz", + "integrity": "sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==", + "license": "MIT" + }, + "node_modules/class-validator": { + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/class-validator/-/class-validator-0.14.2.tgz", + "integrity": "sha512-3kMVRF2io8N8pY1IFIXlho9r8IPUUIfHe2hYVtiebvAzU2XeQFXTv+XI4WX+TnXmtwXMDcjngcpkiPM0O9PvLw==", + "license": "MIT", + "dependencies": { + "@types/validator": "^13.11.8", + "libphonenumber-js": "^1.11.1", + "validator": "^13.9.0" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/concat-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", + "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", + "engines": [ + "node >= 6.0" + ], + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.0.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/confbox": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.2.tgz", + "integrity": "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==", + "license": "MIT", + "optional": true, + "peer": true + }, + "node_modules/consola": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", + "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": "^14.18.0 || >=16.10.0" + } + }, + "node_modules/content-disposition": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz", + "integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deepmerge-ts": { + "version": "7.1.5", + "resolved": "https://registry.npmjs.org/deepmerge-ts/-/deepmerge-ts-7.1.5.tgz", + "integrity": "sha512-HOJkrhaYsweh+W+e74Yn7YStZOilkoPb6fycpwNLKzSPtruFs48nYis0zy5yJz1+ktUhHxoRDJ27RQAWLIJVJw==", + "license": "BSD-3-Clause", + "optional": true, + "peer": true, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/defu": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.4.tgz", + "integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==", + "license": "MIT", + "optional": true, + "peer": true + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destr": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/destr/-/destr-2.0.5.tgz", + "integrity": "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==", + "license": "MIT", + "optional": true, + "peer": true + }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/dotenv": { + "version": "17.2.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.1.tgz", + "integrity": "sha512-kQhDYKZecqnM0fCnzI5eIv5L4cAe/iRI+HqMbO/hbRdTAeXDG+M9FjipUxNfbARuEg4iHIbhnhs78BCHNbSxEQ==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/effect": { + "version": "3.16.12", + "resolved": "https://registry.npmjs.org/effect/-/effect-3.16.12.tgz", + "integrity": "sha512-N39iBk0K71F9nb442TLbTkjl24FLUzuvx2i1I2RsEAQsdAdUTuUoW0vlfUXgkMTUOnYqKnWcFfqw4hK4Pw27hg==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "@standard-schema/spec": "^1.0.0", + "fast-check": "^3.23.1" + } + }, + "node_modules/empathic": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/empathic/-/empathic-2.0.0.tgz", + "integrity": "sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA==", + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz", + "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==", + "license": "MIT", + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.0", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/exsolve": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.7.tgz", + "integrity": "sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw==", + "license": "MIT", + "optional": true, + "peer": true + }, + "node_modules/fast-check": { + "version": "3.23.2", + "resolved": "https://registry.npmjs.org/fast-check/-/fast-check-3.23.2.tgz", + "integrity": "sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "pure-rand": "^6.1.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz", + "integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/giget": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/giget/-/giget-2.0.0.tgz", + "integrity": "sha512-L5bGsVkxJbJgdnwyuheIunkGatUF/zssUoxxjACCseZYAVbaqdh9Tsmmlkl8vYan09H7sbvKt4pS8GqKLBrEzA==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "citty": "^0.1.6", + "consola": "^3.4.0", + "defu": "^6.1.4", + "node-fetch-native": "^1.6.6", + "nypm": "^0.6.0", + "pathe": "^2.0.3" + }, + "bin": { + "giget": "dist/cli.mjs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "license": "MIT", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-errors/node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ignore-by-default": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", + "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", + "dev": true, + "license": "ISC" + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "license": "MIT" + }, + "node_modules/jiti": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.5.1.tgz", + "integrity": "sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w==", + "license": "MIT", + "optional": true, + "peer": true, + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/jsonwebtoken": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "license": "MIT", + "dependencies": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jwa": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.2.tgz", + "integrity": "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==", + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "^1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "license": "MIT", + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/libphonenumber-js": { + "version": "1.12.17", + "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.12.17.tgz", + "integrity": "sha512-bsxi8FoceAYR/bjHcLYc2ShJ/aVAzo5jaxAYiMHF0BD+NTp47405CGuPNKYpw+lHadN9k/ClFGc9X5vaZswIrA==", + "license": "MIT" + }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", + "license": "MIT" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", + "license": "MIT" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", + "license": "MIT" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", + "license": "MIT" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "license": "MIT" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", + "license": "MIT" + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", + "license": "MIT" + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true, + "license": "ISC" + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz", + "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "license": "MIT", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/multer": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/multer/-/multer-2.0.2.tgz", + "integrity": "sha512-u7f2xaZ/UG8oLXHvtF/oWTRvT44p9ecwBBqTwgJVq0+4BW1g8OW01TyMEGWBHbyMOYVHXslaut7qEQ1meATXgw==", + "license": "MIT", + "dependencies": { + "append-field": "^1.0.0", + "busboy": "^1.6.0", + "concat-stream": "^2.0.0", + "mkdirp": "^0.5.6", + "object-assign": "^4.1.1", + "type-is": "^1.6.18", + "xtend": "^4.0.2" + }, + "engines": { + "node": ">= 10.16.0" + } + }, + "node_modules/multer/node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/multer/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/multer/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/multer/node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/node-addon-api": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.5.0.tgz", + "integrity": "sha512-/bRZty2mXUIFY/xU5HLvveNHlswNJej+RnxBjOMkidWfwZzgTbPG1E3K5TOxRLOR+5hX7bSofy8yf1hZevMS8A==", + "license": "MIT", + "engines": { + "node": "^18 || ^20 || >= 21" + } + }, + "node_modules/node-fetch-native": { + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.7.tgz", + "integrity": "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==", + "license": "MIT", + "optional": true, + "peer": true + }, + "node_modules/node-gyp-build": { + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz", + "integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==", + "license": "MIT", + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, + "node_modules/nodemon": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.10.tgz", + "integrity": "sha512-WDjw3pJ0/0jMFmyNDp3gvY2YizjLmmOUQo6DEBY+JgdvW/yQ9mEeSw6H5ythl5Ny2ytb7f9C2nIbjSxMNzbJXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": "^3.5.2", + "debug": "^4", + "ignore-by-default": "^1.0.1", + "minimatch": "^3.1.2", + "pstree.remy": "^1.1.8", + "semver": "^7.5.3", + "simple-update-notifier": "^2.0.0", + "supports-color": "^5.5.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.5" + }, + "bin": { + "nodemon": "bin/nodemon.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nodemon" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/nypm": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/nypm/-/nypm-0.6.2.tgz", + "integrity": "sha512-7eM+hpOtrKrBDCh7Ypu2lJ9Z7PNZBdi/8AT3AX8xoCj43BBVHD0hPSTEvMtkMpfs8FCqBGhxB+uToIQimA111g==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "citty": "^0.1.6", + "consola": "^3.4.2", + "pathe": "^2.0.3", + "pkg-types": "^2.3.0", + "tinyexec": "^1.0.1" + }, + "bin": { + "nypm": "dist/cli.mjs" + }, + "engines": { + "node": "^14.16.0 || >=16.10.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/ohash": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/ohash/-/ohash-2.0.11.tgz", + "integrity": "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==", + "license": "MIT", + "optional": true, + "peer": true + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-to-regexp": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz", + "integrity": "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==", + "license": "MIT", + "engines": { + "node": ">=16" + } + }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "license": "MIT", + "optional": true, + "peer": true + }, + "node_modules/perfect-debounce": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz", + "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==", + "license": "MIT", + "optional": true, + "peer": true + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pkg-types": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.3.0.tgz", + "integrity": "sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "confbox": "^0.2.2", + "exsolve": "^1.0.7", + "pathe": "^2.0.3" + } + }, + "node_modules/prisma": { + "version": "6.16.1", + "resolved": "https://registry.npmjs.org/prisma/-/prisma-6.16.1.tgz", + "integrity": "sha512-MFkMU0eaDDKAT4R/By2IA9oQmwLTxokqv2wegAErr9Rf+oIe7W2sYpE/Uxq0H2DliIR7vnV63PkC1bEwUtl98w==", + "hasInstallScript": true, + "license": "Apache-2.0", + "optional": true, + "peer": true, + "dependencies": { + "@prisma/config": "6.16.1", + "@prisma/engines": "6.16.1" + }, + "bin": { + "prisma": "build/index.js" + }, + "engines": { + "node": ">=18.18" + }, + "peerDependencies": { + "typescript": ">=5.1.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/pstree.remy": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", + "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", + "dev": true, + "license": "MIT" + }, + "node_modules/pure-rand": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", + "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT", + "optional": true, + "peer": true + }, + "node_modules/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.0.tgz", + "integrity": "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==", + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.6.3", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/rc9": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/rc9/-/rc9-2.1.2.tgz", + "integrity": "sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "defu": "^6.1.4", + "destr": "^2.0.3" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/reflect-metadata": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", + "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==", + "license": "Apache-2.0" + }, + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz", + "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==", + "license": "MIT", + "dependencies": { + "debug": "^4.3.5", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "mime-types": "^3.0.1", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/serve-static": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz", + "integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==", + "license": "MIT", + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/simple-update-notifier": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", + "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/streamsearch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/tinyexec": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.1.tgz", + "integrity": "sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw==", + "license": "MIT", + "optional": true, + "peer": true + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/touch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz", + "integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==", + "dev": true, + "license": "ISC", + "bin": { + "nodetouch": "bin/nodetouch.js" + } + }, + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/type-is": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", + "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", + "license": "MIT", + "dependencies": { + "content-type": "^1.0.5", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", + "license": "MIT" + }, + "node_modules/typescript": { + "version": "5.9.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz", + "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", + "devOptional": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undefsafe": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", + "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", + "dev": true, + "license": "MIT" + }, + "node_modules/undici-types": { + "version": "7.12.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.12.0.tgz", + "integrity": "sha512-goOacqME2GYyOZZfb5Lgtu+1IDmAlAEu5xnD3+xTzS10hT0vzpf0SPjkXwAw9Jm+4n/mQGDP3LO8CPbYROeBfQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true, + "license": "MIT" + }, + "node_modules/validator": { + "version": "13.15.15", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.15.15.tgz", + "integrity": "sha512-BgWVbCI72aIQy937xbawcs+hrVaN/CZ2UwutgaJ36hGqRrLNM+f5LUT/YPRbo8IV/ASeFzXszezV+y2+rq3l8A==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "license": "MIT", + "engines": { + "node": ">=0.4" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + } + } +} diff --git a/5-Sprint-Misson/package.json b/5-Sprint-Misson/package.json new file mode 100644 index 000000000..032e9919e --- /dev/null +++ b/5-Sprint-Misson/package.json @@ -0,0 +1,35 @@ +{ + "name": "5-sprint-misson", + "version": "1.0.0", + "type": "module", + "main": "server.js", + "scripts": { + "build": "tsc", + "start": "node dist/server.js", + "dev": "nodemon --exec ts-node src/server.ts" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "@prisma/client": "^6.13.0", + "bcrypt": "^6.0.0", + "class-transformer": "^0.5.1", + "class-validator": "^0.14.2", + "dotenv": "^17.2.1", + "express": "^5.1.0", + "jsonwebtoken": "^9.0.2", + "multer": "^2.0.2", + "reflect-metadata": "^0.2.2" + }, + "description": "", + "devDependencies": { + "@types/bcrypt": "^6.0.0", + "@types/express": "^5.0.3", + "@types/jsonwebtoken": "^9.0.10", + "@types/node": "^24.5.0", + "nodemon": "^3.1.10", + "ts-node": "^10.9.2", + "typescript": "^5.9.2" + } +} diff --git a/5-Sprint-Misson/prisma/migrations/20250729043254_init_simple_blog/migration.sql b/5-Sprint-Misson/prisma/migrations/20250729043254_init_simple_blog/migration.sql new file mode 100644 index 000000000..d5c4624a3 --- /dev/null +++ b/5-Sprint-Misson/prisma/migrations/20250729043254_init_simple_blog/migration.sql @@ -0,0 +1,26 @@ +-- CreateTable +CREATE TABLE "User" ( + "id" SERIAL NOT NULL, + "nickname" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "User_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "Post" ( + "id" SERIAL NOT NULL, + "title" TEXT NOT NULL, + "content" TEXT NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + "views" INTEGER NOT NULL DEFAULT 0, + + CONSTRAINT "Post_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE UNIQUE INDEX "User_nickname_key" ON "User"("nickname"); + +-- CreateIndex +CREATE UNIQUE INDEX "Post_title_key" ON "Post"("title"); diff --git a/5-Sprint-Misson/prisma/migrations/20250801053712_init/migration.sql b/5-Sprint-Misson/prisma/migrations/20250801053712_init/migration.sql new file mode 100644 index 000000000..4bc4ab5ca --- /dev/null +++ b/5-Sprint-Misson/prisma/migrations/20250801053712_init/migration.sql @@ -0,0 +1,36 @@ +/* + Warnings: + + - You are about to drop the `Post` table. If the table is not empty, all the data it contains will be lost. + - You are about to drop the `User` table. If the table is not empty, all the data it contains will be lost. + +*/ +-- DropTable +DROP TABLE "public"."Post"; + +-- DropTable +DROP TABLE "public"."User"; + +-- CreateTable +CREATE TABLE "public"."Product" ( + "id" SERIAL NOT NULL, + "name" TEXT NOT NULL, + "description" TEXT NOT NULL, + "price" INTEGER NOT NULL, + "tags" TEXT NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "Product_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "public"."Article" ( + "id" SERIAL NOT NULL, + "title" TEXT NOT NULL, + "content" TEXT NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "Article_pkey" PRIMARY KEY ("id") +); diff --git a/5-Sprint-Misson/prisma/migrations/20250804002706_add_comment_model/migration.sql b/5-Sprint-Misson/prisma/migrations/20250804002706_add_comment_model/migration.sql new file mode 100644 index 000000000..d477f7171 --- /dev/null +++ b/5-Sprint-Misson/prisma/migrations/20250804002706_add_comment_model/migration.sql @@ -0,0 +1,10 @@ +-- CreateTable +CREATE TABLE "public"."comment" ( + "id" SERIAL NOT NULL, + "content" TEXT NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "productId" INTEGER, + "articleId" INTEGER, + + CONSTRAINT "comment_pkey" PRIMARY KEY ("id") +); diff --git a/5-Sprint-Misson/prisma/migrations/20250917020714_add_tags_array/migration.sql b/5-Sprint-Misson/prisma/migrations/20250917020714_add_tags_array/migration.sql new file mode 100644 index 000000000..bc76fe68c --- /dev/null +++ b/5-Sprint-Misson/prisma/migrations/20250917020714_add_tags_array/migration.sql @@ -0,0 +1,79 @@ +/* + Warnings: + + - The `tags` column on the `Product` table would be dropped and recreated. This will lead to data loss if there is data in the column. + - Added the required column `userId` to the `Article` table without a default value. This is not possible if the table is not empty. + - Added the required column `userId` to the `Product` table without a default value. This is not possible if the table is not empty. + - Added the required column `userId` to the `comment` table without a default value. This is not possible if the table is not empty. + +*/ +-- AlterTable +ALTER TABLE "public"."Article" ADD COLUMN "userId" INTEGER NOT NULL; + +-- AlterTable +ALTER TABLE "public"."Product" ADD COLUMN "userId" INTEGER NOT NULL, +ALTER COLUMN "price" SET DATA TYPE DOUBLE PRECISION, +DROP COLUMN "tags", +ADD COLUMN "tags" TEXT[], +ALTER COLUMN "updatedAt" DROP DEFAULT; + +-- AlterTable +ALTER TABLE "public"."comment" ADD COLUMN "userId" INTEGER NOT NULL; + +-- CreateTable +CREATE TABLE "public"."User" ( + "id" SERIAL NOT NULL, + "email" TEXT NOT NULL, + "nickname" TEXT NOT NULL, + "image" TEXT, + "password" TEXT NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + "refreshToken" TEXT, + + CONSTRAINT "User_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "public"."Like" ( + "id" SERIAL NOT NULL, + "userId" INTEGER NOT NULL, + "productId" INTEGER, + "articleId" INTEGER, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "Like_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE UNIQUE INDEX "User_email_key" ON "public"."User"("email"); + +-- CreateIndex +CREATE UNIQUE INDEX "Like_userId_productId_key" ON "public"."Like"("userId", "productId"); + +-- CreateIndex +CREATE UNIQUE INDEX "Like_userId_articleId_key" ON "public"."Like"("userId", "articleId"); + +-- AddForeignKey +ALTER TABLE "public"."Product" ADD CONSTRAINT "Product_userId_fkey" FOREIGN KEY ("userId") REFERENCES "public"."User"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "public"."Article" ADD CONSTRAINT "Article_userId_fkey" FOREIGN KEY ("userId") REFERENCES "public"."User"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "public"."comment" ADD CONSTRAINT "comment_userId_fkey" FOREIGN KEY ("userId") REFERENCES "public"."User"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "public"."comment" ADD CONSTRAINT "comment_productId_fkey" FOREIGN KEY ("productId") REFERENCES "public"."Product"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "public"."comment" ADD CONSTRAINT "comment_articleId_fkey" FOREIGN KEY ("articleId") REFERENCES "public"."Article"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "public"."Like" ADD CONSTRAINT "Like_userId_fkey" FOREIGN KEY ("userId") REFERENCES "public"."User"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "public"."Like" ADD CONSTRAINT "Like_productId_fkey" FOREIGN KEY ("productId") REFERENCES "public"."Product"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "public"."Like" ADD CONSTRAINT "Like_articleId_fkey" FOREIGN KEY ("articleId") REFERENCES "public"."Article"("id") ON DELETE SET NULL ON UPDATE CASCADE; diff --git a/5-Sprint-Misson/prisma/migrations/migration_lock.toml b/5-Sprint-Misson/prisma/migrations/migration_lock.toml new file mode 100644 index 000000000..044d57cdb --- /dev/null +++ b/5-Sprint-Misson/prisma/migrations/migration_lock.toml @@ -0,0 +1,3 @@ +# Please do not edit this file manually +# It should be added in your version-control system (e.g., Git) +provider = "postgresql" diff --git a/5-Sprint-Misson/prisma/schema.prisma b/5-Sprint-Misson/prisma/schema.prisma new file mode 100644 index 000000000..457e3d8b6 --- /dev/null +++ b/5-Sprint-Misson/prisma/schema.prisma @@ -0,0 +1,81 @@ +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") +} + +generator client { + provider = "prisma-client-js" +} + +model User { + id Int @id @default(autoincrement()) + email String @unique + nickname String + image String? + password String + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + refreshToken String? //토큰 + products Product[] + comments comment[] + articles Article[] + likes Like[] +} + + +model Product { + id Int @id @default(autoincrement()) + name String + description String + price Float + tags String[] + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + userId Int + user User @relation(fields: [userId], references: [id]) + comments comment[] + likes Like[] +} + +model Article { + id Int @id @default(autoincrement()) + title String + content String + userId Int + createdAt DateTime @default(now()) + updatedAt DateTime @default(now()) + + user User @relation(fields: [userId], references: [id]) + comments comment[] + likes Like[] +} + +model comment { + id Int @id @default(autoincrement()) + content String + createdAt DateTime @default(now()) + userId Int + user User @relation(fields: [userId], references: [id]) + productId Int? + product Product? @relation(fields: [productId], references: [id]) + articleId Int? + article Article? @relation(fields: [articleId], references: [id]) +} + +model Like { + id Int @id @default(autoincrement()) + userId Int + productId Int? + articleId Int? + createdAt DateTime @default(now()) + + user User @relation(fields: [userId], references: [id]) + product Product? @relation(fields: [productId], references: [id]) + article Article? @relation(fields: [articleId], references: [id]) + + @@unique([userId, productId]) + @@unique([userId, articleId]) +} + diff --git a/5-Sprint-Misson/product.js b/5-Sprint-Misson/product.js new file mode 100644 index 000000000..195814ace --- /dev/null +++ b/5-Sprint-Misson/product.js @@ -0,0 +1,148 @@ +import express from 'express'; +import { PrismaClient } from '@prisma/client'; + +const router = express.Router(); +const prisma = new PrismaClient(); + + +// 상품 등록 API +router.post('/', async (req, res) => { + const { name, description, price, tags } = req.body; + + try { + if (!name || !description || !price) { + return res.status(400).json({ error: '요소 누락 됨' }); + } + + const product = await prisma.product.create({ + data: { name, description, price, tags }, + }); + + res.status(201).json(product); + } catch (err) { + res.status(500).json({ error: '상품 등록 오류' }); + } +}); + + +// 상품 상세 조회 API +router.get('/:id', async (req, res) => { + const id = Number(req.params.id); + try { + const product = await prisma.product.findUnique({ + where: { id }, + select: { + id: true, + name: true, + description: true, + price: true, + tags: true, + createdAt: true, + }, + }); + + if (!product) { + return res.status(404).json({ error: '상품을 찾을 수 없음.' }); + } + + res.status(200).json(product); + } catch (err) { + res.status(500).json({ error: '상품 조회 중 오류' }); + } +}); + + +// 상품 수정 API +router.patch('/:id', async (req, res) => { + const id = Number(req.params.id); + const { name, description, price, tags } = req.body; + + try { + const exists = await prisma.product.findUnique({ where: { id } }); + if (!exists) return res.status(404).json({ error: '상품 없음' }); + + const product = await prisma.product.update({ + where: { id }, + data: { name, description, price, tags }, + }); + + res.status(200).json(product); + } catch (err) { + res.status(500).json({ error: '수정 중 오류' }); + } +}); + + +// 상품 삭제 API +router.delete('/:id', async (req, res) => { + const id = Number(req.params.id); + try { + const exists = await prisma.product.findUnique({ where: { id } }); + if (!exists) return res.status(404).json({ error: '상품 없음' }); + + await prisma.product.delete({ where: { id } }); + res.status(204).send(); + } catch (err) { + res.status(500).json({ error: '삭제 중 오류' }); + } +}); + + +// 상품 목록 조회 API, offsset 방식 페이지네이션, 최신순 정렬 +router.get('/', async (req, res) => { + const { + page = 1, + pageSize = 10, + sort = 'recent', + search = '', + } = req.query; + + //page와 pageSize로 offset 기반 페이지 나눔진행 + const skip = (Number(page) - 1) * Number(pageSize); + const take = Number(pageSize); + + //검색, name 또는 description에 포함되면 해당 상품 검색 + // insensitive: 대소문자 무시 + try { + const where = { + OR: [ + { name: { contains: search, mode: 'insensitive' } }, + { description: { contains: search, mode: 'insensitive' } }, + ], + }; + + const products = await prisma.product.findMany({ + where, + //최신순 정렬 + orderBy: { createdAt: sort === 'recent' ? 'desc' : 'asc' }, + skip, + take, + select: { + id: true, + name: true, + price: true, + createdAt: true, + }, + }); + + //조건에 맞는 전체 데이터 수 산출 + const total = await prisma.product.count({ where }); + + //상품 목록, pagination 내 정보 보이게 + res.status(200).json({ + data: products, + pagination: { + total, + page: Number(page), + pageSize: Number(pageSize), + totalPages: Math.ceil(total / pageSize), + }, + }); + } catch (err) { + res.status(500).json({ error: '조회 오류' }); + } +}); + + + +export default router; diff --git a/5-Sprint-Misson/src/controllers/articleController.ts b/5-Sprint-Misson/src/controllers/articleController.ts new file mode 100644 index 000000000..2f9a28491 --- /dev/null +++ b/5-Sprint-Misson/src/controllers/articleController.ts @@ -0,0 +1,67 @@ +import { Request, Response } from "express"; +import { ArticlesService } from "../services/articlesService"; + +const service = new ArticlesService(); + +export class ArticlesController { + async create(req: Request, res: Response) { + try { + if (!req.user) return res.status(401).json({ error: "인증 필요" }); + const { title, content } = req.body; + const article = await service.create({ title, content, userId: req.user.id }); + res.status(201).json(article); + } catch (err: any) { + res.status(500).json({ error: err.message }); + } + } + + async getById(req: Request, res: Response) { + try { + const article = await service.getById(Number(req.params.id)); + if (!article) return res.status(404).json({ error: "게시글 없음" }); + res.json(article); + } catch (err: any) { + res.status(500).json({ error: err.message }); + } + } + + async update(req: Request, res: Response) { + try { + if (!req.user) return res.status(401).json({ error: "인증 필요" }); + const { title, content } = req.body; + const updated = await service.update({ + id: Number(req.params.id), + title, + content, + userId: req.user.id + }); + res.json(updated); + } catch (err: any) { + res.status(500).json({ error: err.message }); + } + } + + async delete(req: Request, res: Response) { + try { + if (!req.user) return res.status(401).json({ error: "인증 필요" }); + await service.delete(Number(req.params.id), req.user.id); + res.status(204).send(); + } catch (err: any) { + res.status(500).json({ error: err.message }); + } + } + + async list(req: Request, res: Response) { + try { + const page = Number(req.query.page ?? 1); + const pageSize = Number(req.query.pageSize ?? 10); + const sort = (req.query.sort as "recent" | "asc") ?? "recent"; + const search = (req.query.search as string) ?? ""; + + const result = await service.list(page, pageSize, sort, search); + res.json(result); + } catch (err: any) { + res.status(500).json({ error: err.message }); + } + } +} diff --git a/5-Sprint-Misson/src/controllers/authController.ts b/5-Sprint-Misson/src/controllers/authController.ts new file mode 100644 index 000000000..7b6f6e683 --- /dev/null +++ b/5-Sprint-Misson/src/controllers/authController.ts @@ -0,0 +1,91 @@ +import { Request, Response } from "express"; +import prisma from "../utils/prisma"; +import bcrypt from "bcrypt"; +import { + generateAccessToken, + generateRefreshToken, + verifyRefreshToken, +} from "../utils/jwt"; + +export class AuthController { + // 로그인 + async login(req: Request, res: Response): Promise { + const { email, password } = req.body as { email: string; password: string }; + + try { + const user = await prisma.user.findUnique({ where: { email } }); + if (!user) { + res.status(404).json({ error: "존재하지 않는 이메일" }); + return; + } + + const isMatch = await bcrypt.compare(password, user.password); + if (!isMatch) { + res.status(401).json({ error: "비밀번호 불일치" }); + return; + } + + const accessToken = generateAccessToken(user); + const refreshToken = generateRefreshToken(user); + + await prisma.user.update({ + where: { id: user.id }, + data: { refreshToken }, + }); + + res.json({ accessToken, refreshToken }); + } catch (err) { + res.status(500).json({ error: "로그인 실패" }); + } + } + + // 토큰 갱신 + async refresh(req: Request, res: Response): Promise { + const { refreshToken } = req.body as { refreshToken: string }; + if (!refreshToken) { + res.status(400).json({ error: "Refresh Token 필요" }); + return; + } + + try { + const payload = verifyRefreshToken(refreshToken) as { id: number }; + + const user = await prisma.user.findUnique({ where: { id: payload.id } }); + if (!user || user.refreshToken !== refreshToken) { + res.status(403).json({ error: "Refresh Token 불일치" }); + return; + } + + const newAccessToken = generateAccessToken(user); + const newRefreshToken = generateRefreshToken(user); + + await prisma.user.update({ + where: { id: user.id }, + data: { refreshToken: newRefreshToken }, + }); + + res.json({ accessToken: newAccessToken, refreshToken: newRefreshToken }); + } catch { + res.status(403).json({ error: "Refresh Token 검증 실패" }); + } + } + + // 로그아웃 + async logout(req: Request, res: Response): Promise { + try { + if (!req.user) { + res.status(401).json({ error: "인증 필요" }); + return; + } + + await prisma.user.update({ + where: { id: req.user.id }, + data: { refreshToken: null }, + }); + + res.json({ message: "로그아웃 완료" }); + } catch { + res.status(500).json({ error: "로그아웃 실패" }); + } + } +} diff --git a/5-Sprint-Misson/src/controllers/commentsController.ts b/5-Sprint-Misson/src/controllers/commentsController.ts new file mode 100644 index 000000000..c222d5d85 --- /dev/null +++ b/5-Sprint-Misson/src/controllers/commentsController.ts @@ -0,0 +1,74 @@ +import { Request, Response } from "express"; +import { CommentsService } from "../services/commentsService"; +import { CreateCommentDto, UpdateCommentDto } from "../dtos/comment.dto"; + +export class CommentsController { + private service = new CommentsService(); + + async createForProduct(req: Request, res: Response) { + try { + const dto: CreateCommentDto = req.body; + const comment = await this.service.createForProduct(req.user!.id, Number(req.params.productId), dto.content); + res.status(201).json(comment); + } catch (err: any) { + res.status(500).json({ error: err.message || "상품 댓글 등록 실패" }); + } + } + + async createForArticle(req: Request, res: Response) { + try { + const dto: CreateCommentDto = req.body; + const comment = await this.service.createForArticle(req.user!.id, Number(req.params.articleId), dto.content); + res.status(201).json(comment); + } catch (err: any) { + res.status(500).json({ error: err.message || "게시글 댓글 등록 실패" }); + } + } + + async update(req: Request, res: Response) { + try { + const dto: UpdateCommentDto = req.body; + const updated = await this.service.updateComment(Number(req.params.id), req.user!.id, dto.content); + res.json(updated); + } catch (err: any) { + res.status(err.message === "권한 없음" ? 403 : 500).json({ error: err.message }); + } + } + + async delete(req: Request, res: Response) { + try { + await this.service.deleteComment(Number(req.params.id), req.user!.id); + res.json({ message: "삭제 완료" }); + } catch (err: any) { + res.status(err.message === "권한 없음" ? 403 : 500).json({ error: err.message }); + } + } + + async listForProduct(req: Request, res: Response) { + try { + const { cursor, take } = req.query; + const comments = await this.service.listForProduct( + Number(req.params.productId), + cursor ? Number(cursor) : undefined, + take ? Number(take) : 10 + ); + res.json(comments); + } catch (err: any) { + res.status(500).json({ error: "조회 실패" }); + } + } + + async listForArticle(req: Request, res: Response) { + try { + const { cursor, take } = req.query; + const comments = await this.service.listForArticle( + Number(req.params.articleId), + cursor ? Number(cursor) : undefined, + take ? Number(take) : 10 + ); + res.json(comments); + } catch (err: any) { + res.status(500).json({ error: "조회 실패" }); + } + } +} diff --git a/5-Sprint-Misson/src/controllers/likeController.ts b/5-Sprint-Misson/src/controllers/likeController.ts new file mode 100644 index 000000000..27760aa90 --- /dev/null +++ b/5-Sprint-Misson/src/controllers/likeController.ts @@ -0,0 +1,35 @@ +import { Request, Response } from "express"; +import { LikeService } from "../services/likeService"; + +export class LikeController { + private service = new LikeService(); + + async toggleProductLike(req: Request, res: Response) { + try { + const productId = Number(req.params.productId); + const result = await this.service.toggleProductLike(req.user!.id, productId); + res.json(result); + } catch (err: any) { + res.status(500).json({ error: "상품 좋아요 처리 실패" }); + } + } + + async toggleArticleLike(req: Request, res: Response) { + try { + const articleId = Number(req.params.articleId); + const result = await this.service.toggleArticleLike(req.user!.id, articleId); + res.json(result); + } catch (err: any) { + res.status(500).json({ error: "게시글 좋아요 처리 실패" }); + } + } + + async getUserLikedProducts(req: Request, res: Response) { + try { + const products = await this.service.getUserLikedProducts(req.user!.id); + res.json(products); + } catch (err: any) { + res.status(500).json({ error: "좋아요 상품 조회 실패" }); + } + } +} diff --git a/5-Sprint-Misson/src/controllers/productController.ts b/5-Sprint-Misson/src/controllers/productController.ts new file mode 100644 index 000000000..1c92d16e3 --- /dev/null +++ b/5-Sprint-Misson/src/controllers/productController.ts @@ -0,0 +1,95 @@ +import { Request, Response } from "express"; +import { ProductService } from "../services/productService"; +import { CreateProductDto, UpdateProductDto } from "../dtos/product.dto"; +import { validate } from "class-validator"; + +const productService = new ProductService(); + +export const createProduct = async (req: Request, res: Response) => { + try { + if (!req.user) return res.status(401).json({ error: "로그인 필요" }); + + const dto = Object.assign(new CreateProductDto(), req.body); + const errors = await validate(dto); + if (errors.length > 0) { + return res.status(400).json({ error: errors }); + } + + const product = await productService.createProduct(req.user.id, dto); + res.status(201).json(product); + } catch (err) { + console.error(err); + res.status(500).json({ error: "상품 등록 오류" }); + } +}; + +export const getProductById = async (req: Request, res: Response) => { + try { + const id = Number(req.params.id); + const product = await productService.getProductById(id, req.user?.id); + if (!product) return res.status(404).json({ error: "상품 없음" }); + res.status(200).json(product); + } catch (err) { + console.error(err); + res.status(500).json({ error: "상품 조회 실패" }); + } +}; + +export const updateProduct = async (req: Request, res: Response) => { + try { + if (!req.user) return res.status(401).json({ error: "로그인 필요" }); + + const id = Number(req.params.id); + const dto = Object.assign(new UpdateProductDto(), req.body); + + const errors = await validate(dto); + if (errors.length > 0) { + return res.status(400).json({ error: errors }); + } + + const updated = await productService.updateProduct(id, req.user.id, dto); + if (!updated) return res.status(404).json({ error: "상품 없음" }); + + res.status(200).json(updated); + } catch (err: any) { + console.error(err); + if (err.message === "FORBIDDEN") { + return res.status(403).json({ error: "권한 없음" }); + } + res.status(500).json({ error: "수정 중 오류" }); + } +}; + +export const deleteProduct = async (req: Request, res: Response) => { + try { + if (!req.user) return res.status(401).json({ error: "로그인 필요" }); + + const id = Number(req.params.id); + const deleted = await productService.deleteProduct(id, req.user.id); + if (!deleted) return res.status(404).json({ error: "상품 없음" }); + + res.status(204).send(); + } catch (err: any) { + console.error(err); + if (err.message === "FORBIDDEN") { + return res.status(403).json({ error: "권한 없음" }); + } + res.status(500).json({ error: "삭제 중 오류" }); + } +}; + +export const getProducts = async (req: Request, res: Response) => { + try { + const { page = "1", pageSize = "10", sort = "recent", search = "" } = req.query; + const result = await productService.getProducts( + Number(page), + Number(pageSize), + sort === "recent" ? "recent" : "asc", + String(search) + ); + res.status(200).json(result); + } catch (err) { + console.error(err); + res.status(500).json({ error: "조회 오류" }); + } +}; diff --git a/5-Sprint-Misson/src/controllers/usersController.ts b/5-Sprint-Misson/src/controllers/usersController.ts new file mode 100644 index 000000000..81a377419 --- /dev/null +++ b/5-Sprint-Misson/src/controllers/usersController.ts @@ -0,0 +1,50 @@ +import { Request, Response } from "express"; +import { UserService } from "../services/userService"; +import { UpdateProfileDto, ChangePasswordDto, PaginationQueryDto } from "../dtos/user.dto"; + +export class UsersController { + private service = new UserService(); + + async getProfile(req: Request, res: Response) { + try { + const user = await this.service.getProfile(req.user!.id); + res.json(user); + } catch (err: any) { + res.status(500).json({ error: "내 정보 조회 실패" }); + } + } + + async updateProfile(req: Request, res: Response) { + try { + const dto: UpdateProfileDto = req.body; + const updated = await this.service.updateProfile(req.user!.id, dto); + res.json(updated); + } catch (err: any) { + res.status(500).json({ error: "내 정보 수정 실패" }); + } + } + + async changePassword(req: Request, res: Response) { + try { + const dto: ChangePasswordDto = req.body; + const result = await this.service.changePassword(req.user!.id, dto.oldPassword, dto.newPassword); + res.json(result); + } catch (err: any) { + res.status(err.message === "기존 비밀번호 불일치" ? 400 : err.message === "유저 없음" ? 404 : 500).json({ error: err.message }); + } + } + + async getMyProducts(req: Request, res: Response) { + try { + const query: PaginationQueryDto = req.query; + const products = await this.service.getMyProducts( + req.user!.id, + query.page ? Number(query.page) : 1, + query.pageSize ? Number(query.pageSize) : 10 + ); + res.json(products); + } catch (err: any) { + res.status(500).json({ error: "내 상품 목록 조회 실패" }); + } + } +} diff --git a/5-Sprint-Misson/src/dtos/article.dto.ts b/5-Sprint-Misson/src/dtos/article.dto.ts new file mode 100644 index 000000000..220db9f64 --- /dev/null +++ b/5-Sprint-Misson/src/dtos/article.dto.ts @@ -0,0 +1,12 @@ +export interface CreateArticleDTO { + title: string; + content: string; + userId: number; +} + +export interface UpdateArticleDTO { + id: number; + title?: string; + content?: string; + userId: number; +} diff --git a/5-Sprint-Misson/src/dtos/comment.dto.ts b/5-Sprint-Misson/src/dtos/comment.dto.ts new file mode 100644 index 000000000..8050b5192 --- /dev/null +++ b/5-Sprint-Misson/src/dtos/comment.dto.ts @@ -0,0 +1,7 @@ +export interface CreateCommentDto { + content: string; +} + +export interface UpdateCommentDto { + content: string; +} diff --git a/5-Sprint-Misson/src/dtos/like.dto.ts b/5-Sprint-Misson/src/dtos/like.dto.ts new file mode 100644 index 000000000..3bbf0630c --- /dev/null +++ b/5-Sprint-Misson/src/dtos/like.dto.ts @@ -0,0 +1,3 @@ +export interface ToggleLikeDto { + +} \ No newline at end of file diff --git a/5-Sprint-Misson/src/dtos/product.dto.ts b/5-Sprint-Misson/src/dtos/product.dto.ts new file mode 100644 index 000000000..27238c4bc --- /dev/null +++ b/5-Sprint-Misson/src/dtos/product.dto.ts @@ -0,0 +1,34 @@ +import { IsNotEmpty, IsNumber, IsOptional, IsString } from "class-validator"; + +export class CreateProductDto { + @IsString() + @IsNotEmpty() + name!: string; + + @IsString() + @IsNotEmpty() + description!: string; + + @IsNumber() + price!: number; + + @IsOptional() + tags?: string[]; +} + +export class UpdateProductDto { + @IsOptional() + @IsString() + name?: string; + + @IsOptional() + @IsString() + description?: string; + + @IsOptional() + @IsNumber() + price?: number; + + @IsOptional() + tags?: string[]; +} diff --git a/5-Sprint-Misson/src/dtos/user.dto.ts b/5-Sprint-Misson/src/dtos/user.dto.ts new file mode 100644 index 000000000..5c5ff6b3b --- /dev/null +++ b/5-Sprint-Misson/src/dtos/user.dto.ts @@ -0,0 +1,17 @@ +// 내 정보 수정 DTO +export interface UpdateProfileDto { + nickname?: string; + image?: string; +} + +// 비밀번호 변경 DTO +export interface ChangePasswordDto { + oldPassword: string; + newPassword: string; +} + +// 페이지네이션 쿼리 DTO +export interface PaginationQueryDto { + page?: number; + pageSize?: number; +} diff --git a/5-Sprint-Misson/src/index.js b/5-Sprint-Misson/src/index.js new file mode 100644 index 000000000..e58d3bcb9 --- /dev/null +++ b/5-Sprint-Misson/src/index.js @@ -0,0 +1,49 @@ +import express from 'express'; +import { PrismaClient } from '@prisma/client'; +import productRouter from './routes/product.js'; +import commentRouter from './routes/comment.js'; +import multer from 'multer'; +import cors from 'cors'; + + + +const app = express(); +const prisma = new PrismaClient(); + +app.use(express.json()); +app.use('/products', productRouter); +app.use('/comments', commentRouter); +app.use(cors()); + + +// 이미지 업로드 API +const storage = multer.diskStorage({ + destination: (req, file, cb) => { + cb(null, 'uploads/'); // 업로드 폴더 + }, + filename: (req, file, cb) => { + const unique = Date.now() + '-' + Math.round(Math.random() * 1e9); + const ext = path.extname(file.originalname); + cb(null, unique + ext); + } +}); +const upload = multer({ storage }); + +app.post('/photos', upload.single('image'), (req, res) => { + const filename = req.file.filename; + res.status(200).json({ path: `/photos/${filename}` }); +}); + +app.use('/photos', express.static('uploads')); + + +// 에러 체크 +app.use((err, req, res, next) => { + console.error(err.stack); + res.status(500).json({ error: '서버에 오류가 발생했습니다.' }); +}); + +app.listen(3000, () => { + console.log(`Server running on http://localhost:3000`); +}); + diff --git a/5-Sprint-Misson/src/middlewares/auth.ts b/5-Sprint-Misson/src/middlewares/auth.ts new file mode 100644 index 000000000..c91eb7984 --- /dev/null +++ b/5-Sprint-Misson/src/middlewares/auth.ts @@ -0,0 +1,25 @@ +import { Request, Response, NextFunction } from "express"; +import jwt from "jsonwebtoken"; +import prisma from "../utils/prisma"; + +const ACCESS_SECRET = process.env.ACCESS_SECRET || "change_this_secret"; + +export async function authenticate(req: Request, res: Response, next: NextFunction) { + const header = req.headers.authorization; + const token = header && header.startsWith("Bearer ") ? header.split(" ")[1] : undefined; + + if (!token) return res.status(401).json({ error: "인증 필요" }); + + try { + const payload = jwt.verify(token, ACCESS_SECRET) as { id: number; email?: string }; + const user = await prisma.user.findUnique({ where: { id: payload.id } }); + + if (!user) return res.status(401).json({ error: "유효하지 않은 토큰" }); + + req.user = user; + next(); + } catch (err) { + console.error(err); + return res.status(401).json({ error: "토큰 검증 실패" }); + } +} diff --git a/5-Sprint-Misson/src/repositories/articlesRepository.ts b/5-Sprint-Misson/src/repositories/articlesRepository.ts new file mode 100644 index 000000000..809724e23 --- /dev/null +++ b/5-Sprint-Misson/src/repositories/articlesRepository.ts @@ -0,0 +1,62 @@ +import { PrismaClient, Prisma } from "@prisma/client"; +import { CreateArticleDTO, UpdateArticleDTO } from "../dtos/article.dto"; + +const prisma = new PrismaClient(); + +export class ArticlesRepository { + async create(data: CreateArticleDTO) { + return prisma.article.create({ data }); + } + + async findById(id: number) { + return prisma.article.findUnique({ + where: { id }, + select: { + id: true, + title: true, + content: true, + createdAt: true, + updatedAt: true, + user: { select: { id: true, email: true } } + } + }); + } + + async update(data: UpdateArticleDTO) { + return prisma.article.update({ + where: { id: data.id }, + data: { title: data.title, content: data.content } + }); + } + + async delete(id: number) { + return prisma.article.delete({ where: { id } }); + } + + async list(skip: number, take: number, search: string, sort: "recent" | "asc") { + const where: Prisma.ArticleWhereInput = search + ? { + OR: [ + { title: { contains: search, mode: "insensitive" } }, + { content: { contains: search, mode: "insensitive" } } + ] + } + : {}; + + const articles = await prisma.article.findMany({ + where, + orderBy: { createdAt: sort === "recent" ? "desc" : "asc" }, + skip, + take, + select: { + id: true, + title: true, + createdAt: true, + user: { select: { id: true, email: true } } + } + }); + + const total = await prisma.article.count({ where }); + return { articles, total }; + } +} diff --git a/5-Sprint-Misson/src/routes/articleRoutes.ts b/5-Sprint-Misson/src/routes/articleRoutes.ts new file mode 100644 index 000000000..2b4300fad --- /dev/null +++ b/5-Sprint-Misson/src/routes/articleRoutes.ts @@ -0,0 +1,14 @@ +import { Router } from "express"; +import { ArticlesController } from "../controllers/articleController"; + +const router = Router(); +const controller = new ArticlesController(); + +// 라우터 정의 +router.post("/", controller.create.bind(controller)); +router.get("/:id", controller.getById.bind(controller)); +router.put("/:id", controller.update.bind(controller)); +router.delete("/:id", controller.delete.bind(controller)); +router.get("/", controller.list.bind(controller)); + +export default router; diff --git a/5-Sprint-Misson/src/routes/authRoutes.ts b/5-Sprint-Misson/src/routes/authRoutes.ts new file mode 100644 index 000000000..71944dda0 --- /dev/null +++ b/5-Sprint-Misson/src/routes/authRoutes.ts @@ -0,0 +1,17 @@ +import express from "express"; +import { AuthController } from "../controllers/authController"; +import { authenticate } from "../middlewares/auth"; + +const router = express.Router(); +const controller = new AuthController(); + +// 로그인 +router.post("/login", controller.login.bind(controller)); + +// 토큰 갱신 +router.post("/refresh", controller.refresh.bind(controller)); + +// 로그아웃 - Access Token 필요 +router.post("/logout", authenticate, controller.logout.bind(controller)); + +export default router; diff --git a/5-Sprint-Misson/src/routes/commentRoutes.ts b/5-Sprint-Misson/src/routes/commentRoutes.ts new file mode 100644 index 000000000..30a5d9239 --- /dev/null +++ b/5-Sprint-Misson/src/routes/commentRoutes.ts @@ -0,0 +1,20 @@ +import express from "express"; +import { CommentsController } from "../controllers/commentsController"; +import { authenticate } from "../middlewares/auth"; + +const router = express.Router(); +const controller = new CommentsController(); + +// 등록 - 로그인 필요 +router.post("/product/:productId", authenticate, controller.createForProduct.bind(controller)); +router.post("/article/:articleId", authenticate, controller.createForArticle.bind(controller)); + +// 수정/삭제 - 작성자만 가능 +router.patch("/:id", authenticate, controller.update.bind(controller)); +router.delete("/:id", authenticate, controller.delete.bind(controller)); + +// 조회 - 인증 X +router.get("/product/:productId", controller.listForProduct.bind(controller)); +router.get("/article/:articleId", controller.listForArticle.bind(controller)); + +export default router; diff --git a/5-Sprint-Misson/src/routes/likeRoutes.ts b/5-Sprint-Misson/src/routes/likeRoutes.ts new file mode 100644 index 000000000..892b46cd0 --- /dev/null +++ b/5-Sprint-Misson/src/routes/likeRoutes.ts @@ -0,0 +1,17 @@ +import express from "express"; +import { LikeController } from "../controllers/likeController"; +import { authenticate } from "../middlewares/auth"; + +const router = express.Router(); +const controller = new LikeController(); + +// 상품 좋아요 토글 +router.post("/products/:productId/like", authenticate, controller.toggleProductLike.bind(controller)); + +// 게시글 좋아요 토글 +router.post("/articles/:articleId/like", authenticate, controller.toggleArticleLike.bind(controller)); + +// 유저가 좋아요한 상품 목록 조회 +router.get("/me/products", authenticate, controller.getUserLikedProducts.bind(controller)); + +export default router; diff --git a/5-Sprint-Misson/src/routes/productRoutes.ts b/5-Sprint-Misson/src/routes/productRoutes.ts new file mode 100644 index 000000000..0cfa46c63 --- /dev/null +++ b/5-Sprint-Misson/src/routes/productRoutes.ts @@ -0,0 +1,19 @@ +import express from "express"; +import { + createProduct, + getProductById, + updateProduct, + deleteProduct, + getProducts, +} from "../controllers/productController"; +import { authenticate } from "../middlewares/auth"; + +const router = express.Router(); + +router.post("/", authenticate, createProduct); +router.get("/:id", getProductById); +router.patch("/:id", authenticate, updateProduct); +router.delete("/:id", authenticate, deleteProduct); +router.get("/", getProducts); + +export default router; diff --git a/5-Sprint-Misson/src/routes/usersRoutes.ts b/5-Sprint-Misson/src/routes/usersRoutes.ts new file mode 100644 index 000000000..60f6630e1 --- /dev/null +++ b/5-Sprint-Misson/src/routes/usersRoutes.ts @@ -0,0 +1,18 @@ +import express from "express"; +import { UsersController } from "../controllers/usersController"; +import { authenticate } from "../middlewares/auth"; + +const router = express.Router(); +const controller = new UsersController(); + +// 내 정보 조회 / 수정 +router.get("/me", authenticate, controller.getProfile.bind(controller)); +router.patch("/me", authenticate, controller.updateProfile.bind(controller)); + +// 비밀번호 변경 +router.patch("/me/password", authenticate, controller.changePassword.bind(controller)); + +// 내가 등록한 상품 목록 +router.get("/me/products", authenticate, controller.getMyProducts.bind(controller)); + +export default router; diff --git a/5-Sprint-Misson/src/server.ts b/5-Sprint-Misson/src/server.ts new file mode 100644 index 000000000..26f3bcf80 --- /dev/null +++ b/5-Sprint-Misson/src/server.ts @@ -0,0 +1,24 @@ + +import express, { Application } from "express"; +import dotenv from "dotenv"; + +import authRoutes from "./routes/authRoutes"; +import articleRoutes from "./routes/articleRoutes"; +import commentRoutes from "./routes/commentRoutes"; +import productRoutes from "./routes/productRoutes"; +import usersRoutes from "./routes/usersRoutes"; + +dotenv.config(); + +const app: Application = express(); +app.use(express.json()); + +app.use("/auth", authRoutes); +app.use("/articles", articleRoutes); +app.use("/comments", commentRoutes); +app.use("/products", productRoutes); +app.use("/users", usersRoutes); + +app.listen(3000, () => { + console.log("Server running on http://localhost:3000"); +}); diff --git a/5-Sprint-Misson/src/services/articlesService.ts b/5-Sprint-Misson/src/services/articlesService.ts new file mode 100644 index 000000000..54e974e17 --- /dev/null +++ b/5-Sprint-Misson/src/services/articlesService.ts @@ -0,0 +1,45 @@ +import { ArticlesRepository } from "../repositories/articlesRepository"; +import { CreateArticleDTO, UpdateArticleDTO } from "../dtos/article.dto"; + +const repo = new ArticlesRepository(); + +export class ArticlesService { + async create(data: CreateArticleDTO) { + return repo.create(data); + } + + async getById(id: number) { + return repo.findById(id); + } + + async update(data: UpdateArticleDTO) { + const article = await repo.findById(data.id); + if (!article) throw new Error("게시글 없음"); + if (article.user.id !== data.userId) throw new Error("권한 없음"); + + return repo.update(data); + } + + async delete(id: number, userId: number) { + const article = await repo.findById(id); + if (!article) throw new Error("게시글 없음"); + if (article.user.id !== userId) throw new Error("권한 없음"); + + return repo.delete(id); + } + + async list(page: number, pageSize: number, sort: "recent" | "asc", search: string) { + const skip = (page - 1) * pageSize; + const { articles, total } = await repo.list(skip, pageSize, search, sort); + + return { + data: articles, + pagination: { + total, + page, + pageSize, + totalPages: Math.ceil(total / pageSize) + } + }; + } +} diff --git a/5-Sprint-Misson/src/services/commentsService.ts b/5-Sprint-Misson/src/services/commentsService.ts new file mode 100644 index 000000000..69181e788 --- /dev/null +++ b/5-Sprint-Misson/src/services/commentsService.ts @@ -0,0 +1,55 @@ +import { PrismaClient } from "@prisma/client"; + +const prisma = new PrismaClient(); + +export class CommentsService { + async createForProduct(userId: number, productId: number, content: string) { + return prisma.comment.create({ + data: { content, productId, userId }, + }); + } + + async createForArticle(userId: number, articleId: number, content: string) { + return prisma.comment.create({ + data: { content, articleId, userId }, + }); + } + + async updateComment(commentId: number, userId: number, content: string) { + const comment = await prisma.comment.findUnique({ where: { id: commentId } }); + if (!comment) throw new Error("댓글 없음"); + if (comment.userId !== userId) throw new Error("권한 없음"); + + return prisma.comment.update({ where: { id: commentId }, data: { content } }); + } + + async deleteComment(commentId: number, userId: number) { + const comment = await prisma.comment.findUnique({ where: { id: commentId } }); + if (!comment) throw new Error("댓글 없음"); + if (comment.userId !== userId) throw new Error("권한 없음"); + + await prisma.comment.delete({ where: { id: commentId } }); + } + + async listForProduct(productId: number, cursor?: number, take = 10) { + return prisma.comment.findMany({ + where: { productId }, + take, + skip: cursor ? 1 : 0, + cursor: cursor ? { id: cursor } : undefined, + orderBy: { id: "asc" }, + select: { id: true, content: true, createdAt: true, user: { select: { id: true, nickname: true } } }, + }); + } + + async listForArticle(articleId: number, cursor?: number, take = 10) { + return prisma.comment.findMany({ + where: { articleId }, + take, + skip: cursor ? 1 : 0, + cursor: cursor ? { id: cursor } : undefined, + orderBy: { id: "asc" }, + select: { id: true, content: true, createdAt: true, user: { select: { id: true, nickname: true } } }, + }); + } +} diff --git a/5-Sprint-Misson/src/services/likeService.ts b/5-Sprint-Misson/src/services/likeService.ts new file mode 100644 index 000000000..76c6a8a3c --- /dev/null +++ b/5-Sprint-Misson/src/services/likeService.ts @@ -0,0 +1,45 @@ +import { PrismaClient } from "@prisma/client"; + +const prisma = new PrismaClient(); + +export class LikeService { + // 상품 좋아요 토글 + async toggleProductLike(userId: number, productId: number) { + const existing = await prisma.like.findUnique({ + where: { userId_productId: { userId, productId } }, + }); + + if (existing) { + await prisma.like.delete({ where: { id: existing.id } }); + return { message: "상품 좋아요 취소" }; + } else { + await prisma.like.create({ data: { userId, productId } }); + return { message: "상품 좋아요 등록" }; + } + } + + // 게시글 좋아요 토글 + async toggleArticleLike(userId: number, articleId: number) { + const existing = await prisma.like.findUnique({ + where: { userId_articleId: { userId, articleId } }, + }); + + if (existing) { + await prisma.like.delete({ where: { id: existing.id } }); + return { message: "게시글 좋아요 취소" }; + } else { + await prisma.like.create({ data: { userId, articleId } }); + return { message: "게시글 좋아요 등록" }; + } + } + + // 유저가 좋아요 누른 상품 조회 + async getUserLikedProducts(userId: number) { + const likes = await prisma.like.findMany({ + where: { userId, productId: { not: null } }, + include: { product: true }, + }); + + return likes.map((like) => like.product); + } +} diff --git a/5-Sprint-Misson/src/services/productService.ts b/5-Sprint-Misson/src/services/productService.ts new file mode 100644 index 000000000..0fcdcd006 --- /dev/null +++ b/5-Sprint-Misson/src/services/productService.ts @@ -0,0 +1,80 @@ +import prisma from "../utils/prisma"; +import { Prisma } from "@prisma/client"; +import { CreateProductDto, UpdateProductDto } from "../dtos/product.dto"; + +export class ProductService { + async createProduct(userId: number, dto: CreateProductDto) { + return prisma.product.create({ + data: { ...dto, userId }, + }); + } + + async getProductById(id: number, userId?: number) { + const product = await prisma.product.findUnique({ where: { id } }); + if (!product) return null; + + let isLiked = false; + if (userId) { + const like = await prisma.like.findUnique({ + where: { userId_productId: { userId, productId: id } }, + }); + isLiked = !!like; + } + + return { ...product, isLiked }; + } + + async updateProduct(id: number, userId: number, dto: UpdateProductDto) { + const product = await prisma.product.findUnique({ where: { id } }); + if (!product) return null; + if (product.userId !== userId) throw new Error("FORBIDDEN"); + + return prisma.product.update({ + where: { id }, + data: dto, + }); + } + + async deleteProduct(id: number, userId: number) { + const product = await prisma.product.findUnique({ where: { id } }); + if (!product) return null; + if (product.userId !== userId) throw new Error("FORBIDDEN"); + + await prisma.product.delete({ where: { id } }); + return true; + } + + async getProducts(page: number, pageSize: number, sort: "recent" | "asc", search: string) { + const skip = (page - 1) * pageSize; + const take = pageSize; + + const where: Prisma.ProductWhereInput = search + ? { + OR: [ + { name: { contains: search, mode: "insensitive" } }, + { description: { contains: search, mode: "insensitive" } }, + ], + } + : {}; + + const products = await prisma.product.findMany({ + where, + orderBy: { createdAt: sort === "recent" ? "desc" : "asc" }, + skip, + take, + select: { id: true, name: true, price: true, createdAt: true }, + }); + + const total = await prisma.product.count({ where }); + + return { + data: products, + pagination: { + total, + page, + pageSize, + totalPages: Math.ceil(total / pageSize), + }, + }; +} +} diff --git a/5-Sprint-Misson/src/services/userService.ts b/5-Sprint-Misson/src/services/userService.ts new file mode 100644 index 000000000..fcad21023 --- /dev/null +++ b/5-Sprint-Misson/src/services/userService.ts @@ -0,0 +1,73 @@ +import { PrismaClient } from "@prisma/client"; +import bcrypt from "bcrypt"; + +const prisma = new PrismaClient(); + +export class UserService { + async getProfile(userId: number) { + return prisma.user.findUnique({ + where: { id: userId }, + select: { + id: true, + email: true, + nickname: true, + image: true, + createdAt: true, + updatedAt: true, + }, + }); + } + + async updateProfile(userId: number, data: { nickname?: string; image?: string }) { + return prisma.user.update({ + where: { id: userId }, + data, + select: { + id: true, + email: true, + nickname: true, + image: true, + createdAt: true, + updatedAt: true, + }, + }); + } + + async changePassword(userId: number, oldPassword: string, newPassword: string) { + const user = await prisma.user.findUnique({ where: { id: userId } }); + if (!user) throw new Error("유저 없음"); + + const isMatch = await bcrypt.compare(oldPassword, user.password); + if (!isMatch) throw new Error("기존 비밀번호 불일치"); + + const hashed = await bcrypt.hash(newPassword, 10); + await prisma.user.update({ where: { id: userId }, data: { password: hashed } }); + return { message: "비밀번호 변경 완료" }; + } + + async getMyProducts(userId: number, page = 1, pageSize = 10) { + const skip = (page - 1) * pageSize; + const take = pageSize; + + const [products, total] = await Promise.all([ + prisma.product.findMany({ + where: { userId }, + skip, + take, + orderBy: { createdAt: "desc" }, + select: { id: true, name: true, description: true, price: true, tags: true, createdAt: true }, + }), + prisma.product.count({ where: { userId } }), + ]); + + return { + data: products, + pagination: { + total, + page, + pageSize, + totalPages: Math.ceil(total / pageSize), + }, + }; + } +} diff --git a/5-Sprint-Misson/src/types/express/index.d.ts b/5-Sprint-Misson/src/types/express/index.d.ts new file mode 100644 index 000000000..98162fa31 --- /dev/null +++ b/5-Sprint-Misson/src/types/express/index.d.ts @@ -0,0 +1,9 @@ +import { User } from "@prisma/client"; + +declare global { + namespace Express { + interface Request { + user?: User; + } + } +} diff --git a/5-Sprint-Misson/src/uploads/1753935463073-536058091.png b/5-Sprint-Misson/src/uploads/1753935463073-536058091.png new file mode 100644 index 0000000000000000000000000000000000000000..fb52717db5b466ad749b5aa4f14fd314553291b3 GIT binary patch literal 84440 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 Z{vT>UqZv`+i39)u002ovPDHLkV1kJwN^}4K literal 0 HcmV?d00001 diff --git a/5-Sprint-Misson/src/uploads/1753935773083-773169808.jpg b/5-Sprint-Misson/src/uploads/1753935773083-773169808.jpg new file mode 100644 index 0000000000000000000000000000000000000000..4c2f8984f9a662f5d2895584cf3265d619a1f00d GIT binary patch literal 7016 zcmcI|cQBmq_x7^NMs&eqt=>zNh?>=6m58#579xq>SuI2-Aw*qWkm$Yl7DQQHlxVB> z&LW8V^ZEYX&v)jXdH;RSbIqK&&-KiG&NFkDb3M0Hw~GLBHMlAqfP(`7;M_fcTMXbM z;2r@X6%{2l1r-%FH5Dx_BRv>Q0|tX>!88oC^mKF#U?zGdZe}n87Xt$wt1t^E51*i* zAe32DLX=;eTR@Nxhlq%Xl!TO?oE$93$jr$nAkHVq_fUe5Pv9RD=M#8%=lJeCpTu2= zPw=1Ue};T_CU6(JZ39q%06l;nAPy@4mjVY!fpgmlfZP>`3&i=S)_;wF5Qv8l!u^MR zMh?Km0pj8k;t>$yGn-E4UHcN9CtT^0W^;J^GGMS;gc zDNH5L8le3S`QUa2Kzv8yQUECcvVdl5H7%b1e=zEwkDgJOF>kDk2>FQW4p~KuWpQbE z*z7v)`BJgmzylT4qEyGy>5K9gUagE;Oz?sm1GLlkPZ}#_HCKr2v#nKIeF0jMQQxUtzl|ih$C2xB>0zE+^_q z9G*%%AsG87My8V^^f9*7t!IZpZRj4pmU%Qc*1lJ!B4XL{*%)J))JIINk91u*4oGCS zyiYEqS$W>2@6mARIk4r0w^#P$WasH|H4>^Nr;hYTR>jQ6CWdaLn#MchkE8l!DjQJn zSmM11Wwk_&s~+NBIS`43s@MY?6DW-XMug>dU?{Ym8@UfYwKj`|a4#Vk(t`>p0IV8`9B zap`+ePnvDwi(T4Eg_QGUKEv>sfLiw3QaUewx9Y1K*g1aV*GLj1yHs3){6Kh$&tMm3 zYVuKWIk^8f@PMU)F;8kL4S%k2Hkt$^vYQ)S*<9hyf|?_N3sua9*f zzLD1Khzi*Pj3%%7|e zBQ*&2QbtmCn2Y5$qV3+%GT3iKY4T1ji?d&|z8lO=Mt3VSUlrl;KEQ|gx1H;{UFFre z?HwXmTOGf8P!q(1nM5>0xjH9TMQtnq@Ad4B?Uw;q;85i$rAqGuMG>_f`VXR(E~@}y zmo0c-@`Gs-%=4YRRTWxh`5XMT9Q-QMgwELIoe;9mD#r4V+ zjp_squPPak^yz-RY0*rzgDI)pSX9mrX3&Mmy!9IDIL@RikXIvd;G(da!ilXwr-LLg z!|`e#SjN1H1eo+EbuHYI59MB;%>R_|a&ZdJ(TUgEjt>gYWnQqdL~A4P z79bU<{|Y`lB}@?tK1G;Xm4bOAjFG)LIsro2+B$KL)Mj9JGmz$#pO_&b00mCXQx7ZInbh6wl%0%ga1!@4?4BC5l+g2^CXh9m*Z zk72*snWI=eAr$IJhtM~N>ygW@2H%6NQU|F>pYcL3YKc;TKNdYFGq!<~OjHs4#Q11i zzHWqYQ@yJ8RKO~3+R!|tC)0G6$67?r-@aUg3C_j*A?=2GVzHTL-<(1@V0pm(rTm^W z69C_NFVw_L))s*C?xCWJBaDT?8U}_^&)S1kM?oucz^Fi`KNMGgj1Rt4>^es za8#CE5o`E>4{GdKJJ5s%|y*f7$Vsl*| zi$OwI{K)kgB{n?%DMHNxiNoIfON5U#pmARXMXFpsztZ?EeS!XB+19_36j?6&)7`P> zMyCPYD9w>Buwmwr^IGdURNW4?9h=tP4oxayaDT|4#N6p`ZFe3hUHk+zUsl6P$9e7Q z7jO$m(S>m@6uyzvgDtflQ%h*?u>REOQmPWC z{j$T+C?*Dz(^(I&mG`mPJ>4a$w{tI7O{Mm>bMpNKp7jhrl+bPyrUvASLy{m%Etin- zm4cmv(Yz|VN3`UpNv81=*=+uj?_+}f0QqN{jZ&xLPtlDlJm2alV7;=Q8Vl*-g~sBf zk&oX*?pc!y@xCWC;dA^lWLXou{CXp7aV8OqVk5L84x!^GmGQ-5OTcEtg&ncPVS|ra zGp&zX@yHm9d$^8Y4W-YAh_E96J}O+@)|OpQGHg%Rh3-$c-RP)K>-Ty|>0=Jq(=jgk znHa-{!>JXh8L64&=qy)APO~tkTPTJ#^Y0Q|{1{f#)MGyy_Z{&sKAzkHin;9w&B0X0 zLi;~S`_rHEK>Jp_U0{1%44n>ZrX1t@W(~13jtHm1tlj)60Ppmc-b>X-2X3}b6Gtjt z*?KQ{rKwPGLta&`0Z)@6D9TCE{{4w%8t1ORClQNQB_v+q8RJtP5-e^IN%bpjy&jw+ zYO~t>`Au|DO-)tcPpb5-hSXr^CyLFIXF7iEH#N%`OE-qUG)$u<8fEV$xhO@p+#ztq zb?SIMdY4sFoRj=-s(IDo#h0f`6BDVNBh&i1X50p^6D2OCugy|=+YkSgMv*PMk6}Iw z+UNS4Hdiv`Vq)`f0;`f&*aQOT5TXN<(vb>+5^I>p2FtrE5t{zOr7FV*RL7;&v`#y_ z0-55IvbI%hl~I?fWNrlLfmP=-T6&yt&BqtQY1$b-f?9RrVuokPG{GqkJ0pGzG#Oh!TvVJoU zRmXQQikno^;B6M84Z+Mb{AzvqN~P1T0Y9yW1ML$zBhI|ypi^CWzHGmuF-uw@#n-`K zlHF*veo(Fx#-moGBEPO;&jvvGNHP{a8A-oq%5LQ#aPTx456(!ePspNT!5)s2T6o!P zGq8wCXCwJ(4F3c?g;UeXe^Lb!NSMqWx_oM9AdXN*tYqrZOxxabD-(4@CJ-Jz+bci| zy!ymvQk|_)y1L++|EkmXQ%9CyzV@@8XS}QBM%4U0_avUnkt)=`h9iZM_`^i?2k5}< zyi+^Zo*(|g2;t=c8?9K5S%>fl>P|0JhCOx%PqKOVI@qowQ6TFzuv!S&Txc&tNpSME zZ}4y7)$>AHtSTMtfg`9v1zQzHSJJN@*0<@!Ad0`BOt@A#XIgqs3 zmpmw`rufM~U(JnQk!mA@p_3TqpH$K76baGVR^$W76EX*ifY_4CD)daQb9m(b5BLl} z4{b3#Legaeo51GJl_gG-mXt$ba*I?Ipj&|cYknG!?ka34SObT3b<9CL3|e4xnA8Yos6enjN#{GK!8 zv3yc;(%YC%P!*Wb&@JF0FX^YRr*`34)67vz%wcs>6|wrulu_pA@f|#e=KBEU97OJB zmDKkh9ZAQ9L+y>7!=jZUi7aDU3)b#f5Er4W{OZ$CE6!WD+}fU6{}JkEYMkamgLv{eB(qPxxeQi;wM$X zOEESC6dk1AtMB!B$b4-1p z2RAl0ueIj`ou-*Bh^aX#>Qbo@pet z-eTN`Me2HKjX#=&uwUUM0V2IrH;1DJ@;6UpS&YLn)evz+|BK1imPqi7wLZI*$N&a_ zZ>QJ){cb&yc0!+i_~m()Hzxy8&=@p)o+9opTPM14cL;A?<|`L00PId2i~9S268D|D z&vUd<)ekL^-;^a*WA;G=rI%1@JdSXoE(%B6Dkb&HlZYMXz_6W%{>tV^hiG_3+v$ZC z@JD@WqQH8W`SW2J`oG&7UyRy?>-@9Ooci)?R*0)%i_23pe|P2riM7-TSiQ$rBV&<_ zzL_O0T#%rg_4{KS#`H> z2-Ul4!OK=~xZeXU*!P_d~}T8Z;~BUT0R+dVCKx$}*ZG_m`mKjz*OldBUP(#GX2wUy%jJ zhGFS6Zy4$EV0*#=e^VAu0@nm_-xPvog~IlQoN3^|syI(J>rwp`R74MFH81Ck5cY@4 zzH7>1dhDqNejcTD&nyN>Tm;j2gcjwEzEMQJ$d^J|Pd|#49hqZz)+IPvblj%X8%-{c zF9MSG_|3rnE*Oma#8#GMX;`Mh@TqEoIy@!$s{%0!p6T@Y{$ok|4VBu7-j2hFgPbpT zEiEOTn;_qZ?gA@P8W}cOl^h|#Nv3J7pLH_$dnwnFwFwCHKLOdf>0sGp@d_7DOi)M? z;i!>%Gu(NBN|MenX$7#0d8oDuOwf8F?}1#x1Z1tL@zYD6an%)fm#ye51}}p{v1Xax z(~Uo8T?u@69~YB1--x1@ryrC9ku3vT5f^};%Zt$G0(opn&C|0VHezDQ_d%0Qxi7cTu_l1MlU4mV78B@ zX0f(%Sx$GUjxr3^-7OmSkp}^IG~%r=QhCsvfWM5db^9S3V?- zaQs)PTX%G^dK%+7jlyx9a9BipL~*i(8McOyKZIMH8oRgd8yHk|BOiscem?FfXRk|i z13tc_1Yir5KVo@G%nd&qg}z)(klYh--g!7r{2nH`kVH^m+bJc4ElH>nI%%=O{$Q;= zJ(`uRqNh2FehfWwA}lB|{^Ai_sSdW&BvBIweM%SHFlPJ`i2dqao zhnK`;%J;5Fhkw%~C-J%FdJz&yMcHuB4YC?(RyRviYpnWX#walz#XH`xR7{#*5!DEv zl0nxWqx-2_iRIx0$ZeNrZ=51!X~uu7rN@6(zYm(^-#{@7uHI1NP55Q?#*?ys6dK;5 z>Y~EgP8RVCt_mAyD(gXOI$td9(f5b?y%0&_T&t8VzdX-QF@!NpR3n@14tp=AlCmyh zdV@U!#RtuIO-yqR1gg`bED}Y{m^vt2C zoE|v7Ty~8c>@-^*izr#PW(^IHknXZ2%c6C6i6Te;tZhV&!Iw2N`AblbwHQ+@7;KS? zLAwQK-vBHL9N7Xa!xo0T4H72b3o9VC_!Vman z7Q&QG69n@*kH+g^H6=cs;gg~&n{1wMjHBInIP|m;a+mUvlHupaWdjq^*@!$CL(nn^ zOPy-$zCZjkdlI{?>!>}eOmH+1Ewm$r>ovgWsv59LN9!m4r^vXu;8$d#5oUqCybFTr z%bevSI3SDxt-4G3|4sSx+EoVe@BbQnj!8nwJ;zZ1{Hs+ZT%2;i+3)1HY_koRFK4tV z>mhlI%bkEXJBJsM-vT(C=jF1v-WfG;xqDrybqRI$XS7RPcLg<1U=k2e(cK?7qkfdJ z(Y{czg&nGZ4lZDI=f8HFvWK3uO!h2#qx`oNDDSXm{UFWj#+_gl;fxJ&paG`O?sfJM44G zByj_@_nU07B>H`V%KH0K744gvV?ZH$2!GuO=OeE4IwbrOR# zc#nEw{~{iqx0l^5fVV9CQK{|Zc!RfLVgF{+FIDO(pc`9xTxTSPsw?Q5nJT;G0&B*L8=xl07BY~MlsuEaNeHrS z+Wf93+(6p5lbV%nH&Q#VLDM4nEB*0gLd?KUu6A59YbKLhn>k| zmyD%WeS7j#2PBFg$*RgS5LpP6yj~`F?x155(^A*UdRnYE&ivZdcyno)2{Q<>gc9A0 z4?XEL^R`K9OjHo{3^+ZY)Qkpl{@CW5h@#k)(7~K1 zg_}Z-c_@heykv-bY`~O}CbK*SV`-Cr({RFpgn?(8HQb^Ge<$UI5!T z&R?k8nar!W0xb3jhUXRe2Pj};%*bDPPP*euwXzcw6>KYz+L-8P1ra)WH++3nVPGuO z7E~|OMR`%ICrp6ITT~W;FGeA|OvpAqf1%#8_nED0jB##?j-;I{)ZUF~{%N{kv zmV`;Lrw$~V7pO$qI_>kCX8Wk0yLngRv|_ri{p`){=JhJBnk4o`PgOHug1_Q7*tj4_-~dd+?lP3e;Fm z`qSM??B|{&AD(Ka+ye5~_Sp=MaD z%wp=HM`R;Z+@afD??sh!U9ZgqEAy7&Bv(~xFEh6GncS Date: Tue, 4 Nov 2025 09:58:11 +0900 Subject: [PATCH 5/5] Add files via upload --- S_M8/package-lock.json | 4542 +++++++++++++++++ S_M8/package.json | 34 + .../migrations/20250111082621_/migration.sql | 43 + .../20250304075708_add_user/migration.sql | 15 + .../migration.sql | 31 + .../migration.sql | 33 + S_M8/prisma/migrations/migration_lock.toml | 3 + S_M8/prisma/schema.prisma | 102 + S_M8/src/controllers/articlesController.ts | 79 + S_M8/src/controllers/authController.ts | 50 + S_M8/src/controllers/commentsController.ts | 18 + S_M8/src/controllers/errorController.ts | 50 + S_M8/src/controllers/imagesController.ts | 48 + .../controllers/notification-controller.ts | 0 S_M8/src/controllers/productsController.ts | 79 + S_M8/src/controllers/usersController.ts | 58 + S_M8/src/dto/userResponseDTO.ts | 8 + S_M8/src/lib/constants.ts | 12 + S_M8/src/lib/errors/BadRequestError.ts | 8 + S_M8/src/lib/errors/ForbiddenError.ts | 8 + S_M8/src/lib/errors/NotFoundError.ts | 8 + S_M8/src/lib/errors/UnauthorizedError.ts | 8 + S_M8/src/lib/prismaClient.ts | 3 + S_M8/src/lib/socket.ts | 0 S_M8/src/lib/token.ts | 28 + S_M8/src/lib/withAsync.ts | 11 + S_M8/src/main.ts | 33 + S_M8/src/middlewares/authenticate.ts | 23 + S_M8/src/repositories/articlesRepository.ts | 94 + S_M8/src/repositories/commentsRepository.ts | 50 + S_M8/src/repositories/favoritesRepository.ts | 22 + S_M8/src/repositories/likesRepository.ts | 22 + .../repositories/notification-repository.ts | 34 + S_M8/src/repositories/productsRepository.ts | 154 + S_M8/src/repositories/usersRepository.ts | 37 + S_M8/src/routers/articlesRouter.ts | 28 + S_M8/src/routers/authRouter.ts | 12 + S_M8/src/routers/commentsRouter.ts | 11 + S_M8/src/routers/imagesRouter.ts | 9 + S_M8/src/routers/notification-router.ts | 12 + S_M8/src/routers/productsRouter.ts | 28 + S_M8/src/routers/usersRouter.ts | 20 + S_M8/src/services/articlesService.ts | 59 + S_M8/src/services/authService.ts | 102 + S_M8/src/services/commentsService.ts | 135 + S_M8/src/services/favoritesService.ts | 32 + S_M8/src/services/likesService.ts | 32 + S_M8/src/services/notification-service.ts | 39 + S_M8/src/services/productsService.ts | 59 + S_M8/src/services/usersService.ts | 38 + S_M8/src/structs/articlesStructs.ts | 12 + S_M8/src/structs/authStructs.ts | 14 + S_M8/src/structs/commentsStruct.ts | 12 + S_M8/src/structs/commonStructs.ts | 22 + S_M8/src/structs/productsStruct.ts | 14 + S_M8/src/structs/usersStructs.ts | 19 + S_M8/src/types/Article.ts | 13 + S_M8/src/types/Comment.ts | 11 + S_M8/src/types/Product.ts | 15 + S_M8/src/types/User.ts | 11 + S_M8/src/types/express.d.ts | 9 + S_M8/src/types/notification.ts | 9 + S_M8/src/types/pagination.ts | 21 + S_M8/tests/test.http | 188 + S_M8/tests/test.js | 68 + S_M8/tests/test.png | Bin 0 -> 6066 bytes S_M8/tsconfig.json | 116 + 67 files changed, 6918 insertions(+) create mode 100644 S_M8/package-lock.json create mode 100644 S_M8/package.json create mode 100644 S_M8/prisma/migrations/20250111082621_/migration.sql create mode 100644 S_M8/prisma/migrations/20250304075708_add_user/migration.sql create mode 100644 S_M8/prisma/migrations/20250304082334_authorization/migration.sql create mode 100644 S_M8/prisma/migrations/20250304094902_add_favorite_like/migration.sql create mode 100644 S_M8/prisma/migrations/migration_lock.toml create mode 100644 S_M8/prisma/schema.prisma create mode 100644 S_M8/src/controllers/articlesController.ts create mode 100644 S_M8/src/controllers/authController.ts create mode 100644 S_M8/src/controllers/commentsController.ts create mode 100644 S_M8/src/controllers/errorController.ts create mode 100644 S_M8/src/controllers/imagesController.ts create mode 100644 S_M8/src/controllers/notification-controller.ts create mode 100644 S_M8/src/controllers/productsController.ts create mode 100644 S_M8/src/controllers/usersController.ts create mode 100644 S_M8/src/dto/userResponseDTO.ts create mode 100644 S_M8/src/lib/constants.ts create mode 100644 S_M8/src/lib/errors/BadRequestError.ts create mode 100644 S_M8/src/lib/errors/ForbiddenError.ts create mode 100644 S_M8/src/lib/errors/NotFoundError.ts create mode 100644 S_M8/src/lib/errors/UnauthorizedError.ts create mode 100644 S_M8/src/lib/prismaClient.ts create mode 100644 S_M8/src/lib/socket.ts create mode 100644 S_M8/src/lib/token.ts create mode 100644 S_M8/src/lib/withAsync.ts create mode 100644 S_M8/src/main.ts create mode 100644 S_M8/src/middlewares/authenticate.ts create mode 100644 S_M8/src/repositories/articlesRepository.ts create mode 100644 S_M8/src/repositories/commentsRepository.ts create mode 100644 S_M8/src/repositories/favoritesRepository.ts create mode 100644 S_M8/src/repositories/likesRepository.ts create mode 100644 S_M8/src/repositories/notification-repository.ts create mode 100644 S_M8/src/repositories/productsRepository.ts create mode 100644 S_M8/src/repositories/usersRepository.ts create mode 100644 S_M8/src/routers/articlesRouter.ts create mode 100644 S_M8/src/routers/authRouter.ts create mode 100644 S_M8/src/routers/commentsRouter.ts create mode 100644 S_M8/src/routers/imagesRouter.ts create mode 100644 S_M8/src/routers/notification-router.ts create mode 100644 S_M8/src/routers/productsRouter.ts create mode 100644 S_M8/src/routers/usersRouter.ts create mode 100644 S_M8/src/services/articlesService.ts create mode 100644 S_M8/src/services/authService.ts create mode 100644 S_M8/src/services/commentsService.ts create mode 100644 S_M8/src/services/favoritesService.ts create mode 100644 S_M8/src/services/likesService.ts create mode 100644 S_M8/src/services/notification-service.ts create mode 100644 S_M8/src/services/productsService.ts create mode 100644 S_M8/src/services/usersService.ts create mode 100644 S_M8/src/structs/articlesStructs.ts create mode 100644 S_M8/src/structs/authStructs.ts create mode 100644 S_M8/src/structs/commentsStruct.ts create mode 100644 S_M8/src/structs/commonStructs.ts create mode 100644 S_M8/src/structs/productsStruct.ts create mode 100644 S_M8/src/structs/usersStructs.ts create mode 100644 S_M8/src/types/Article.ts create mode 100644 S_M8/src/types/Comment.ts create mode 100644 S_M8/src/types/Product.ts create mode 100644 S_M8/src/types/User.ts create mode 100644 S_M8/src/types/express.d.ts create mode 100644 S_M8/src/types/notification.ts create mode 100644 S_M8/src/types/pagination.ts create mode 100644 S_M8/tests/test.http create mode 100644 S_M8/tests/test.js create mode 100644 S_M8/tests/test.png create mode 100644 S_M8/tsconfig.json diff --git a/S_M8/package-lock.json b/S_M8/package-lock.json new file mode 100644 index 000000000..ba1a7b415 --- /dev/null +++ b/S_M8/package-lock.json @@ -0,0 +1,4542 @@ +{ + "name": "S_M8", + "version": "1.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "dependencies": { + "@prisma/client": "^5.16.2", + "bcrypt": "^5.1.1", + "cookie-parser": "^1.4.7", + "cors": "^2.8.5", + "dotenv": "^16.4.5", + "express": "^4.21.2", + "jsonwebtoken": "^9.0.2", + "multer": "^1.4.5-lts.1", + "socket": "^1.1.25", + "socket.io": "^4.8.1", + "superstruct": "^2.0.2", + "uuid": "^11.0.5" + }, + "devDependencies": { + "@types/bcrypt": "^5.0.2", + "@types/cookie-parser": "^1.4.8", + "@types/cors": "^2.8.17", + "@types/express": "^5.0.0", + "@types/jsonwebtoken": "^9.0.9", + "@types/multer": "^1.4.12", + "nodemon": "^3.1.9", + "prettier": "^3.3.2", + "prisma": "^5.16.2", + "ts-node": "^10.9.2", + "typescript": "^5.8.2" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@mapbox/node-pre-gyp": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz", + "integrity": "sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==", + "dependencies": { + "detect-libc": "^2.0.0", + "https-proxy-agent": "^5.0.0", + "make-dir": "^3.1.0", + "node-fetch": "^2.6.7", + "nopt": "^5.0.0", + "npmlog": "^5.0.1", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.11" + }, + "bin": { + "node-pre-gyp": "bin/node-pre-gyp" + } + }, + "node_modules/@prisma/client": { + "version": "5.16.2", + "resolved": "https://registry.npmjs.org/@prisma/client/-/client-5.16.2.tgz", + "integrity": "sha512-+1lmkhR9gHWcTC5oghm2ZKpWljyWdzfazCVlLKUWXVmwHSf52g81aZ8qb6Km5Bs025yBi7puLp3qSLEvktoUtw==", + "hasInstallScript": true, + "engines": { + "node": ">=16.13" + }, + "peerDependencies": { + "prisma": "*" + }, + "peerDependenciesMeta": { + "prisma": { + "optional": true + } + } + }, + "node_modules/@prisma/debug": { + "version": "5.16.2", + "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-5.16.2.tgz", + "integrity": "sha512-ItzB4nR4O8eLzuJiuP3WwUJfoIvewMHqpGCad+64gvThcKEVOtaUza9AEJo2DPqAOa/AWkFyK54oM4WwHeew+A==", + "devOptional": true + }, + "node_modules/@prisma/engines": { + "version": "5.16.2", + "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-5.16.2.tgz", + "integrity": "sha512-qUxwMtrwoG3byd4PbX6T7EjHJ8AUhzTuwniOGkh/hIznBfcE2QQnGakyEq4VnwNuttMqvh/GgPFapHQ3lCuRHg==", + "devOptional": true, + "hasInstallScript": true, + "dependencies": { + "@prisma/debug": "5.16.2", + "@prisma/engines-version": "5.16.0-24.34ace0eb2704183d2c05b60b52fba5c43c13f303", + "@prisma/fetch-engine": "5.16.2", + "@prisma/get-platform": "5.16.2" + } + }, + "node_modules/@prisma/engines-version": { + "version": "5.16.0-24.34ace0eb2704183d2c05b60b52fba5c43c13f303", + "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-5.16.0-24.34ace0eb2704183d2c05b60b52fba5c43c13f303.tgz", + "integrity": "sha512-HkT2WbfmFZ9WUPyuJHhkiADxazHg8Y4gByrTSVeb3OikP6tjQ7txtSUGu9OBOBH0C13dPKN2qqH12xKtHu/Hiw==", + "devOptional": true + }, + "node_modules/@prisma/fetch-engine": { + "version": "5.16.2", + "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-5.16.2.tgz", + "integrity": "sha512-sq51lfHKfH2jjYSjBtMjP+AznFqOJzXpqmq6B9auWrlTJrMgZ7lPyhWUW7VU7LsQU48/TJ+DZeIz8s9bMYvcHg==", + "devOptional": true, + "dependencies": { + "@prisma/debug": "5.16.2", + "@prisma/engines-version": "5.16.0-24.34ace0eb2704183d2c05b60b52fba5c43c13f303", + "@prisma/get-platform": "5.16.2" + } + }, + "node_modules/@prisma/get-platform": { + "version": "5.16.2", + "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-5.16.2.tgz", + "integrity": "sha512-cXiHPgNLNyj22vLouPVNegklpRL/iX2jxTeap5GRO3DmCoVyIHmJAV1CgUMUJhHlcol9yYy7EHvsnXTDJ/PKEA==", + "devOptional": true, + "dependencies": { + "@prisma/debug": "5.16.2" + } + }, + "node_modules/@socket.io/component-emitter": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", + "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==", + "license": "MIT" + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", + "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", + "dev": true + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true + }, + "node_modules/@types/bcrypt": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@types/bcrypt/-/bcrypt-5.0.2.tgz", + "integrity": "sha512-6atioO8Y75fNcbmj0G7UjI9lXN2pQ/IGJ2FWT4a/btd0Lk9lQalHLKhkgKVZ3r+spnmWUKfbMi1GEe9wyHQfNQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/body-parser": { + "version": "1.19.5", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", + "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", + "dev": true, + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/cookie-parser": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/@types/cookie-parser/-/cookie-parser-1.4.8.tgz", + "integrity": "sha512-l37JqFrOJ9yQfRQkljb41l0xVphc7kg5JTjjr+pLRZ0IyZ49V4BQ8vbF4Ut2C2e+WH4al3xD3ZwYwIUfnbT4NQ==", + "dev": true, + "peerDependencies": { + "@types/express": "*" + } + }, + "node_modules/@types/cors": { + "version": "2.8.17", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.17.tgz", + "integrity": "sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/express": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.0.tgz", + "integrity": "sha512-DvZriSMehGHL1ZNLzi6MidnsDhUZM/x2pRdDIKdwbUNqqwHxMlRdkxtn6/EPKyqKpHqTl/4nRZsRNLpZxZRpPQ==", + "dev": true, + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^5.0.0", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.0.6.tgz", + "integrity": "sha512-3xhRnjJPkULekpSzgtoNYYcTWgEZkp4myc+Saevii5JPnHNvHMRlBSHDbs7Bh1iPPoVTERHEZXyhyLbMEsExsA==", + "dev": true, + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/http-errors": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", + "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==", + "dev": true + }, + "node_modules/@types/jsonwebtoken": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.9.tgz", + "integrity": "sha512-uoe+GxEuHbvy12OUQct2X9JenKM3qAscquYymuQN4fMWG9DBQtykrQEFcAbVACF7qaLw9BePSodUL0kquqBJpQ==", + "dev": true, + "dependencies": { + "@types/ms": "*", + "@types/node": "*" + } + }, + "node_modules/@types/mime": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", + "dev": true + }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "dev": true + }, + "node_modules/@types/multer": { + "version": "1.4.12", + "resolved": "https://registry.npmjs.org/@types/multer/-/multer-1.4.12.tgz", + "integrity": "sha512-pQ2hoqvXiJt2FP9WQVLPRO+AmiIm/ZYkavPlIQnx282u4ZrVdztx0pkh3jjpQt0Kz+YI0YhSG264y08UJKoUQg==", + "dev": true, + "dependencies": { + "@types/express": "*" + } + }, + "node_modules/@types/node": { + "version": "22.13.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.9.tgz", + "integrity": "sha512-acBjXdRJ3A6Pb3tqnw9HZmyR3Fiol3aGxRCK1x3d+6CDAMjl7I649wpSd+yNURCjbOUGu9tqtLKnTGxmK6CyGw==", + "dependencies": { + "undici-types": "~6.20.0" + } + }, + "node_modules/@types/qs": { + "version": "6.9.18", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.18.tgz", + "integrity": "sha512-kK7dgTYDyGqS+e2Q4aK9X3D7q234CIZ1Bv0q/7Z5IwRDoADNU81xXJK/YVyLbLTZCoIwUoDoffFeF+p/eIklAA==", + "dev": true + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "dev": true + }, + "node_modules/@types/send": { + "version": "0.17.4", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", + "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", + "dev": true, + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.15.7", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz", + "integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==", + "dev": true, + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*", + "@types/send": "*" + } + }, + "node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.14.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", + "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "dev": true, + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/agent-base/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/agent-base/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/append-field": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", + "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==" + }, + "node_modules/aproba": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==" + }, + "node_modules/are-we-there-yet": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", + "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", + "deprecated": "This package is no longer supported.", + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/are-we-there-yet/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "node_modules/base64id": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", + "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", + "license": "MIT", + "engines": { + "node": "^4.5.0 || >= 5.9" + } + }, + "node_modules/bcrypt": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-5.1.1.tgz", + "integrity": "sha512-AGBHOG5hPYZ5Xl9KXzU5iKq9516yEmvCKDg3ecP5kX2aB6UqTeXZxk2ELnDgDm6BQSMlLt9rDB4LoSMx0rYwww==", + "hasInstallScript": true, + "dependencies": { + "@mapbox/node-pre-gyp": "^1.0.11", + "node-addon-api": "^5.0.0" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/body-parser": { + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", + "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.13.0", + "raw-body": "2.5.2", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + }, + "node_modules/busboy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "dependencies": { + "streamsearch": "^1.1.0" + }, + "engines": { + "node": ">=10.16.0" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "engines": { + "node": ">=10" + } + }, + "node_modules/color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "bin": { + "color-support": "bin.js" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + }, + "node_modules/concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "engines": [ + "node >= 0.8" + ], + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==" + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-parser": { + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.7.tgz", + "integrity": "sha512-nGUvgXnotP3BsjiLX2ypbQnWoGUPIIfHQNZkkC668ntrzGWEZVW70HDEB1qnNGMicPje6EttlIgzo51YSwNQGw==", + "dependencies": { + "cookie": "0.7.2", + "cookie-signature": "1.0.6" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/cookie-parser/node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==" + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/detect-libc": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", + "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", + "engines": { + "node": ">=8" + } + }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/dotenv": { + "version": "16.4.5", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", + "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/engine.io": { + "version": "6.6.4", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.4.tgz", + "integrity": "sha512-ZCkIjSYNDyGn0R6ewHDtXgns/Zre/NT6Agvq1/WobF7JXgFff4SeDroKiCO3fNJreU9YG429Sc81o4w5ok/W5g==", + "license": "MIT", + "dependencies": { + "@types/cors": "^2.8.12", + "@types/node": ">=10.0.0", + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "~0.7.2", + "cors": "~2.8.5", + "debug": "~4.3.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.17.1" + }, + "engines": { + "node": ">=10.2.0" + } + }, + "node_modules/engine.io-parser": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", + "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/engine.io/node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/engine.io/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/engine.io/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express": { + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", + "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", + "license": "MIT", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.3", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.7.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.3.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.12", + "proxy-addr": "~2.0.7", + "qs": "6.13.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.19.0", + "serve-static": "1.16.2", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/fs-minipass/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gauge": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", + "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", + "deprecated": "This package is no longer supported.", + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.2", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.1", + "object-assign": "^4.1.1", + "signal-exit": "^3.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==" + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "license": "MIT", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/https-proxy-agent/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/https-proxy-agent/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ignore-by-default": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", + "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", + "dev": true + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + }, + "node_modules/jsonwebtoken": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "dependencies": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jsonwebtoken/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" + }, + "node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/multer": { + "version": "1.4.5-lts.1", + "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.5-lts.1.tgz", + "integrity": "sha512-ywPWvcDMeH+z9gQq5qYHCCy+ethsk4goepZ45GLD63fOu0YcNecQxi64nDs3qluZB+murG3/D4dJ7+dGctcCQQ==", + "dependencies": { + "append-field": "^1.0.0", + "busboy": "^1.0.0", + "concat-stream": "^1.5.2", + "mkdirp": "^0.5.4", + "object-assign": "^4.1.1", + "type-is": "^1.6.4", + "xtend": "^4.0.0" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/node-addon-api": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz", + "integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==" + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/nodemon": { + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.9.tgz", + "integrity": "sha512-hdr1oIb2p6ZSxu3PB2JWWYS7ZQ0qvaZsc3hK8DR8f02kRzc8rjYmxAIvdz+aYC+8F2IjNaB7HMcSDg8nQpJxyg==", + "dev": true, + "dependencies": { + "chokidar": "^3.5.2", + "debug": "^4", + "ignore-by-default": "^1.0.1", + "minimatch": "^3.1.2", + "pstree.remy": "^1.1.8", + "semver": "^7.5.3", + "simple-update-notifier": "^2.0.0", + "supports-color": "^5.5.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.5" + }, + "bin": { + "nodemon": "bin/nodemon.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nodemon" + } + }, + "node_modules/nodemon/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/nodemon/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npmlog": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", + "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", + "deprecated": "This package is no longer supported.", + "dependencies": { + "are-we-there-yet": "^2.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^3.0.0", + "set-blocking": "^2.0.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-to-regexp": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", + "license": "MIT" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/prettier": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.2.tgz", + "integrity": "sha512-rAVeHYMcv8ATV5d508CFdn+8/pHPpXeIid1DdrPwXnaAdH7cqjVbpJaT5eq4yRAFU/lsbwYwSF/n5iNrdJHPQA==", + "dev": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prisma": { + "version": "5.16.2", + "resolved": "https://registry.npmjs.org/prisma/-/prisma-5.16.2.tgz", + "integrity": "sha512-rFV/xoBR2hBGGlu4LPLQd4U8WVA+tSAmYyFWGPRVfj+xg7N4kiZV4lSk38htSpF+/IuHKzlrbh4SFk8Z18cI8A==", + "devOptional": true, + "hasInstallScript": true, + "dependencies": { + "@prisma/engines": "5.16.2" + }, + "bin": { + "prisma": "build/index.js" + }, + "engines": { + "node": ">=16.13" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/pstree.remy": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", + "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", + "dev": true + }, + "node_modules/qs": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/readable-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", + "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/serve-static": { + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", + "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", + "license": "MIT", + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.19.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" + }, + "node_modules/simple-update-notifier": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", + "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", + "dev": true, + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/socket": { + "version": "1.1.25", + "resolved": "https://registry.npmjs.org/socket/-/socket-1.1.25.tgz", + "integrity": "sha512-qtZOaQ5oHmZcN2QywxYjClCTC/+ZUZnGWXc8G8AE2voC3HW6mBikWSym2Yk6NYUq1BwMQ84uPaEVZ3+NOmT5Gg==", + "license": "MIT AND OFL-1.1", + "bin": { + "socket": "bin/cli.js", + "socket-npm": "bin/npm-cli.js", + "socket-npx": "bin/npx-cli.js", + "socket-pnpm": "bin/pnpm-cli.js", + "socket-yarn": "bin/yarn-cli.js" + }, + "engines": { + "node": ">=18", + "pnpm": ">=10.16.0" + } + }, + "node_modules/socket.io": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.1.tgz", + "integrity": "sha512-oZ7iUCxph8WYRHHcjBEc9unw3adt5CmSNlppj/5Q4k2RIrhl8Z5yY2Xr4j9zj0+wzVZ0bxmYoGSzKJnRl6A4yg==", + "license": "MIT", + "dependencies": { + "accepts": "~1.3.4", + "base64id": "~2.0.0", + "cors": "~2.8.5", + "debug": "~4.3.2", + "engine.io": "~6.6.0", + "socket.io-adapter": "~2.5.2", + "socket.io-parser": "~4.2.4" + }, + "engines": { + "node": ">=10.2.0" + } + }, + "node_modules/socket.io-adapter": { + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.5.tgz", + "integrity": "sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg==", + "license": "MIT", + "dependencies": { + "debug": "~4.3.4", + "ws": "~8.17.1" + } + }, + "node_modules/socket.io-adapter/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io-adapter/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/socket.io-parser": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", + "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", + "license": "MIT", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-parser/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io-parser/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/socket.io/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/streamsearch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/superstruct": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/superstruct/-/superstruct-2.0.2.tgz", + "integrity": "sha512-uV+TFRZdXsqXTL2pRvujROjdZQ4RAlBUS5BTh9IGm+jTqQntYThciG/qu57Gs69yjnVUSqdxF9YLmSnpupBW9A==", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/tar": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tar/node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/touch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz", + "integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==", + "dev": true, + "bin": { + "nodetouch": "bin/nodetouch.js" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "dev": true, + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==" + }, + "node_modules/typescript": { + "version": "5.8.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz", + "integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undefsafe": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", + "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", + "dev": true + }, + "node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==" + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.0.5.tgz", + "integrity": "sha512-508e6IcKLrhxKdBbcA2b4KQZlLVp2+J5UwQ6F7Drckkc5N9ZJwFa4TgWtsww9UG8fGHbm6gbV19TdM5pQ4GaIA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/esm/bin/uuid" + } + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, + "node_modules/ws": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "engines": { + "node": ">=0.4" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "engines": { + "node": ">=6" + } + } + }, + "dependencies": { + "@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "requires": { + "@jridgewell/trace-mapping": "0.3.9" + } + }, + "@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true + }, + "@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "dev": true + }, + "@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "requires": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "@mapbox/node-pre-gyp": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz", + "integrity": "sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==", + "requires": { + "detect-libc": "^2.0.0", + "https-proxy-agent": "^5.0.0", + "make-dir": "^3.1.0", + "node-fetch": "^2.6.7", + "nopt": "^5.0.0", + "npmlog": "^5.0.1", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.11" + } + }, + "@prisma/client": { + "version": "5.16.2", + "resolved": "https://registry.npmjs.org/@prisma/client/-/client-5.16.2.tgz", + "integrity": "sha512-+1lmkhR9gHWcTC5oghm2ZKpWljyWdzfazCVlLKUWXVmwHSf52g81aZ8qb6Km5Bs025yBi7puLp3qSLEvktoUtw==", + "requires": {} + }, + "@prisma/debug": { + "version": "5.16.2", + "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-5.16.2.tgz", + "integrity": "sha512-ItzB4nR4O8eLzuJiuP3WwUJfoIvewMHqpGCad+64gvThcKEVOtaUza9AEJo2DPqAOa/AWkFyK54oM4WwHeew+A==", + "devOptional": true + }, + "@prisma/engines": { + "version": "5.16.2", + "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-5.16.2.tgz", + "integrity": "sha512-qUxwMtrwoG3byd4PbX6T7EjHJ8AUhzTuwniOGkh/hIznBfcE2QQnGakyEq4VnwNuttMqvh/GgPFapHQ3lCuRHg==", + "devOptional": true, + "requires": { + "@prisma/debug": "5.16.2", + "@prisma/engines-version": "5.16.0-24.34ace0eb2704183d2c05b60b52fba5c43c13f303", + "@prisma/fetch-engine": "5.16.2", + "@prisma/get-platform": "5.16.2" + } + }, + "@prisma/engines-version": { + "version": "5.16.0-24.34ace0eb2704183d2c05b60b52fba5c43c13f303", + "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-5.16.0-24.34ace0eb2704183d2c05b60b52fba5c43c13f303.tgz", + "integrity": "sha512-HkT2WbfmFZ9WUPyuJHhkiADxazHg8Y4gByrTSVeb3OikP6tjQ7txtSUGu9OBOBH0C13dPKN2qqH12xKtHu/Hiw==", + "devOptional": true + }, + "@prisma/fetch-engine": { + "version": "5.16.2", + "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-5.16.2.tgz", + "integrity": "sha512-sq51lfHKfH2jjYSjBtMjP+AznFqOJzXpqmq6B9auWrlTJrMgZ7lPyhWUW7VU7LsQU48/TJ+DZeIz8s9bMYvcHg==", + "devOptional": true, + "requires": { + "@prisma/debug": "5.16.2", + "@prisma/engines-version": "5.16.0-24.34ace0eb2704183d2c05b60b52fba5c43c13f303", + "@prisma/get-platform": "5.16.2" + } + }, + "@prisma/get-platform": { + "version": "5.16.2", + "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-5.16.2.tgz", + "integrity": "sha512-cXiHPgNLNyj22vLouPVNegklpRL/iX2jxTeap5GRO3DmCoVyIHmJAV1CgUMUJhHlcol9yYy7EHvsnXTDJ/PKEA==", + "devOptional": true, + "requires": { + "@prisma/debug": "5.16.2" + } + }, + "@socket.io/component-emitter": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", + "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==" + }, + "@tsconfig/node10": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", + "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", + "dev": true + }, + "@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true + }, + "@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true + }, + "@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true + }, + "@types/bcrypt": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@types/bcrypt/-/bcrypt-5.0.2.tgz", + "integrity": "sha512-6atioO8Y75fNcbmj0G7UjI9lXN2pQ/IGJ2FWT4a/btd0Lk9lQalHLKhkgKVZ3r+spnmWUKfbMi1GEe9wyHQfNQ==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/body-parser": { + "version": "1.19.5", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", + "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", + "dev": true, + "requires": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/cookie-parser": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/@types/cookie-parser/-/cookie-parser-1.4.8.tgz", + "integrity": "sha512-l37JqFrOJ9yQfRQkljb41l0xVphc7kg5JTjjr+pLRZ0IyZ49V4BQ8vbF4Ut2C2e+WH4al3xD3ZwYwIUfnbT4NQ==", + "dev": true, + "requires": {} + }, + "@types/cors": { + "version": "2.8.17", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.17.tgz", + "integrity": "sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==", + "requires": { + "@types/node": "*" + } + }, + "@types/express": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.0.tgz", + "integrity": "sha512-DvZriSMehGHL1ZNLzi6MidnsDhUZM/x2pRdDIKdwbUNqqwHxMlRdkxtn6/EPKyqKpHqTl/4nRZsRNLpZxZRpPQ==", + "dev": true, + "requires": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^5.0.0", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "@types/express-serve-static-core": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.0.6.tgz", + "integrity": "sha512-3xhRnjJPkULekpSzgtoNYYcTWgEZkp4myc+Saevii5JPnHNvHMRlBSHDbs7Bh1iPPoVTERHEZXyhyLbMEsExsA==", + "dev": true, + "requires": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "@types/http-errors": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", + "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==", + "dev": true + }, + "@types/jsonwebtoken": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.9.tgz", + "integrity": "sha512-uoe+GxEuHbvy12OUQct2X9JenKM3qAscquYymuQN4fMWG9DBQtykrQEFcAbVACF7qaLw9BePSodUL0kquqBJpQ==", + "dev": true, + "requires": { + "@types/ms": "*", + "@types/node": "*" + } + }, + "@types/mime": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", + "dev": true + }, + "@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "dev": true + }, + "@types/multer": { + "version": "1.4.12", + "resolved": "https://registry.npmjs.org/@types/multer/-/multer-1.4.12.tgz", + "integrity": "sha512-pQ2hoqvXiJt2FP9WQVLPRO+AmiIm/ZYkavPlIQnx282u4ZrVdztx0pkh3jjpQt0Kz+YI0YhSG264y08UJKoUQg==", + "dev": true, + "requires": { + "@types/express": "*" + } + }, + "@types/node": { + "version": "22.13.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.9.tgz", + "integrity": "sha512-acBjXdRJ3A6Pb3tqnw9HZmyR3Fiol3aGxRCK1x3d+6CDAMjl7I649wpSd+yNURCjbOUGu9tqtLKnTGxmK6CyGw==", + "requires": { + "undici-types": "~6.20.0" + } + }, + "@types/qs": { + "version": "6.9.18", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.18.tgz", + "integrity": "sha512-kK7dgTYDyGqS+e2Q4aK9X3D7q234CIZ1Bv0q/7Z5IwRDoADNU81xXJK/YVyLbLTZCoIwUoDoffFeF+p/eIklAA==", + "dev": true + }, + "@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "dev": true + }, + "@types/send": { + "version": "0.17.4", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", + "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", + "dev": true, + "requires": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "@types/serve-static": { + "version": "1.15.7", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz", + "integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==", + "dev": true, + "requires": { + "@types/http-errors": "*", + "@types/node": "*", + "@types/send": "*" + } + }, + "abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + }, + "accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "requires": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + } + }, + "acorn": { + "version": "8.14.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", + "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", + "dev": true + }, + "acorn-walk": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "dev": true, + "requires": { + "acorn": "^8.11.0" + } + }, + "agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "requires": { + "debug": "4" + }, + "dependencies": { + "debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "requires": { + "ms": "^2.1.3" + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + } + } + }, + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" + }, + "anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "append-field": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", + "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==" + }, + "aproba": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==" + }, + "are-we-there-yet": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", + "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, + "arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "base64id": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", + "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==" + }, + "bcrypt": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-5.1.1.tgz", + "integrity": "sha512-AGBHOG5hPYZ5Xl9KXzU5iKq9516yEmvCKDg3ecP5kX2aB6UqTeXZxk2ELnDgDm6BQSMlLt9rDB4LoSMx0rYwww==", + "requires": { + "@mapbox/node-pre-gyp": "^1.0.11", + "node-addon-api": "^5.0.0" + } + }, + "binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true + }, + "body-parser": { + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", + "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", + "requires": { + "bytes": "3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.13.0", + "raw-body": "2.5.2", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + } + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "requires": { + "fill-range": "^7.1.1" + } + }, + "buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" + }, + "buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + }, + "busboy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "requires": { + "streamsearch": "^1.1.0" + } + }, + "bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==" + }, + "call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "requires": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + } + }, + "call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "requires": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + } + }, + "chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "requires": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + } + }, + "chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==" + }, + "color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==" + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + }, + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==" + }, + "content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "requires": { + "safe-buffer": "5.2.1" + } + }, + "content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==" + }, + "cookie": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==" + }, + "cookie-parser": { + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.7.tgz", + "integrity": "sha512-nGUvgXnotP3BsjiLX2ypbQnWoGUPIIfHQNZkkC668ntrzGWEZVW70HDEB1qnNGMicPje6EttlIgzo51YSwNQGw==", + "requires": { + "cookie": "0.7.2", + "cookie-signature": "1.0.6" + }, + "dependencies": { + "cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==" + } + } + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" + }, + "core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + }, + "cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "requires": { + "object-assign": "^4", + "vary": "^1" + } + }, + "create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==" + }, + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" + }, + "destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==" + }, + "detect-libc": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", + "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==" + }, + "diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true + }, + "dotenv": { + "version": "16.4.5", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", + "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==" + }, + "dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "requires": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + } + }, + "ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==" + }, + "engine.io": { + "version": "6.6.4", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.4.tgz", + "integrity": "sha512-ZCkIjSYNDyGn0R6ewHDtXgns/Zre/NT6Agvq1/WobF7JXgFff4SeDroKiCO3fNJreU9YG429Sc81o4w5ok/W5g==", + "requires": { + "@types/cors": "^2.8.12", + "@types/node": ">=10.0.0", + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "~0.7.2", + "cors": "~2.8.5", + "debug": "~4.3.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.17.1" + }, + "dependencies": { + "cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==" + }, + "debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "requires": { + "ms": "^2.1.3" + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + } + } + }, + "engine.io-parser": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", + "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==" + }, + "es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==" + }, + "es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==" + }, + "es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "requires": { + "es-errors": "^1.3.0" + } + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==" + }, + "express": { + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", + "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", + "requires": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.3", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.7.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.3.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.12", + "proxy-addr": "~2.0.7", + "qs": "6.13.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.19.0", + "serve-static": "1.16.2", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + } + }, + "fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "finalhandler": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", + "requires": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + } + }, + "forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==" + }, + "fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "requires": { + "minipass": "^3.0.0" + }, + "dependencies": { + "minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "requires": { + "yallist": "^4.0.0" + } + } + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" + }, + "fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "optional": true + }, + "function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==" + }, + "gauge": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", + "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", + "requires": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.2", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.1", + "object-assign": "^4.1.1", + "signal-exit": "^3.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.2" + } + }, + "get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "requires": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + } + }, + "get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "requires": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + } + }, + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==" + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true + }, + "has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==" + }, + "has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==" + }, + "hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "requires": { + "function-bind": "^1.1.2" + } + }, + "http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "requires": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + } + }, + "https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "requires": { + "agent-base": "6", + "debug": "4" + }, + "dependencies": { + "debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "requires": { + "ms": "^2.1.3" + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + } + } + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ignore-by-default": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", + "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" + }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" + }, + "is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + }, + "jsonwebtoken": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "requires": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "dependencies": { + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + } + } + }, + "jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "requires": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "requires": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, + "lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" + }, + "lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" + }, + "lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==" + }, + "lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" + }, + "lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" + }, + "lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" + }, + "lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" + }, + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "requires": { + "semver": "^6.0.0" + }, + "dependencies": { + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" + } + } + }, + "make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==" + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==" + }, + "merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==" + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==" + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" + }, + "mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" + }, + "mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "requires": { + "mime-db": "1.52.0" + } + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==" + }, + "minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==" + }, + "minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "requires": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "dependencies": { + "minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "requires": { + "yallist": "^4.0.0" + } + } + } + }, + "mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "requires": { + "minimist": "^1.2.6" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "multer": { + "version": "1.4.5-lts.1", + "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.5-lts.1.tgz", + "integrity": "sha512-ywPWvcDMeH+z9gQq5qYHCCy+ethsk4goepZ45GLD63fOu0YcNecQxi64nDs3qluZB+murG3/D4dJ7+dGctcCQQ==", + "requires": { + "append-field": "^1.0.0", + "busboy": "^1.0.0", + "concat-stream": "^1.5.2", + "mkdirp": "^0.5.4", + "object-assign": "^4.1.1", + "type-is": "^1.6.4", + "xtend": "^4.0.0" + } + }, + "negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==" + }, + "node-addon-api": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz", + "integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==" + }, + "node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "requires": { + "whatwg-url": "^5.0.0" + } + }, + "nodemon": { + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.9.tgz", + "integrity": "sha512-hdr1oIb2p6ZSxu3PB2JWWYS7ZQ0qvaZsc3hK8DR8f02kRzc8rjYmxAIvdz+aYC+8F2IjNaB7HMcSDg8nQpJxyg==", + "dev": true, + "requires": { + "chokidar": "^3.5.2", + "debug": "^4", + "ignore-by-default": "^1.0.1", + "minimatch": "^3.1.2", + "pstree.remy": "^1.1.8", + "semver": "^7.5.3", + "simple-update-notifier": "^2.0.0", + "supports-color": "^5.5.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.5" + }, + "dependencies": { + "debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dev": true, + "requires": { + "ms": "^2.1.3" + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + } + } + }, + "nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "requires": { + "abbrev": "1" + } + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "npmlog": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", + "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", + "requires": { + "are-we-there-yet": "^2.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^3.0.0", + "set-blocking": "^2.0.0" + } + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==" + }, + "object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==" + }, + "on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "requires": { + "ee-first": "1.1.1" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "requires": { + "wrappy": "1" + } + }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==" + }, + "path-to-regexp": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==" + }, + "picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true + }, + "prettier": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.2.tgz", + "integrity": "sha512-rAVeHYMcv8ATV5d508CFdn+8/pHPpXeIid1DdrPwXnaAdH7cqjVbpJaT5eq4yRAFU/lsbwYwSF/n5iNrdJHPQA==", + "dev": true + }, + "prisma": { + "version": "5.16.2", + "resolved": "https://registry.npmjs.org/prisma/-/prisma-5.16.2.tgz", + "integrity": "sha512-rFV/xoBR2hBGGlu4LPLQd4U8WVA+tSAmYyFWGPRVfj+xg7N4kiZV4lSk38htSpF+/IuHKzlrbh4SFk8Z18cI8A==", + "devOptional": true, + "requires": { + "@prisma/engines": "5.16.2" + } + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "requires": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + } + }, + "pstree.remy": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", + "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", + "dev": true + }, + "qs": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "requires": { + "side-channel": "^1.0.6" + } + }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" + }, + "raw-body": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "requires": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + } + }, + "readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + } + } + }, + "readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "requires": { + "picomatch": "^2.2.1" + } + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "requires": { + "glob": "^7.1.3" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "semver": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==" + }, + "send": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", + "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", + "requires": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "dependencies": { + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==" + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + } + } + }, + "serve-static": { + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", + "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", + "requires": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.19.0" + } + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" + }, + "setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "requires": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + } + }, + "side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "requires": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + } + }, + "side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "requires": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + } + }, + "side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "requires": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + } + }, + "signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" + }, + "simple-update-notifier": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", + "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", + "dev": true, + "requires": { + "semver": "^7.5.3" + } + }, + "socket": { + "version": "1.1.25", + "resolved": "https://registry.npmjs.org/socket/-/socket-1.1.25.tgz", + "integrity": "sha512-qtZOaQ5oHmZcN2QywxYjClCTC/+ZUZnGWXc8G8AE2voC3HW6mBikWSym2Yk6NYUq1BwMQ84uPaEVZ3+NOmT5Gg==" + }, + "socket.io": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.1.tgz", + "integrity": "sha512-oZ7iUCxph8WYRHHcjBEc9unw3adt5CmSNlppj/5Q4k2RIrhl8Z5yY2Xr4j9zj0+wzVZ0bxmYoGSzKJnRl6A4yg==", + "requires": { + "accepts": "~1.3.4", + "base64id": "~2.0.0", + "cors": "~2.8.5", + "debug": "~4.3.2", + "engine.io": "~6.6.0", + "socket.io-adapter": "~2.5.2", + "socket.io-parser": "~4.2.4" + }, + "dependencies": { + "debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "requires": { + "ms": "^2.1.3" + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + } + } + }, + "socket.io-adapter": { + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.5.tgz", + "integrity": "sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg==", + "requires": { + "debug": "~4.3.4", + "ws": "~8.17.1" + }, + "dependencies": { + "debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "requires": { + "ms": "^2.1.3" + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + } + } + }, + "socket.io-parser": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", + "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", + "requires": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + }, + "dependencies": { + "debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "requires": { + "ms": "^2.1.3" + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + } + } + }, + "statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" + }, + "streamsearch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + } + } + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "superstruct": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/superstruct/-/superstruct-2.0.2.tgz", + "integrity": "sha512-uV+TFRZdXsqXTL2pRvujROjdZQ4RAlBUS5BTh9IGm+jTqQntYThciG/qu57Gs69yjnVUSqdxF9YLmSnpupBW9A==" + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "tar": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", + "requires": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "dependencies": { + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" + } + } + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, + "toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" + }, + "touch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz", + "integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==", + "dev": true + }, + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "dev": true, + "requires": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + } + }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==" + }, + "typescript": { + "version": "5.8.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz", + "integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==", + "dev": true + }, + "undefsafe": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", + "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", + "dev": true + }, + "undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==" + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==" + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==" + }, + "uuid": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.0.5.tgz", + "integrity": "sha512-508e6IcKLrhxKdBbcA2b4KQZlLVp2+J5UwQ6F7Drckkc5N9ZJwFa4TgWtsww9UG8fGHbm6gbV19TdM5pQ4GaIA==" + }, + "v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==" + }, + "webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "requires": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, + "ws": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "requires": {} + }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true + } + } +} diff --git a/S_M8/package.json b/S_M8/package.json new file mode 100644 index 000000000..6341e8787 --- /dev/null +++ b/S_M8/package.json @@ -0,0 +1,34 @@ +{ + "scripts": { + "start": "node ./build/main.js", + "build": "tsc", + "dev": "nodemon ./src/main.ts --watch ./src" + }, + "devDependencies": { + "@types/bcrypt": "^5.0.2", + "@types/cookie-parser": "^1.4.8", + "@types/cors": "^2.8.17", + "@types/express": "^5.0.0", + "@types/jsonwebtoken": "^9.0.9", + "@types/multer": "^1.4.12", + "nodemon": "^3.1.9", + "prettier": "^3.3.2", + "prisma": "^5.16.2", + "ts-node": "^10.9.2", + "typescript": "^5.8.2" + }, + "dependencies": { + "@prisma/client": "^5.16.2", + "bcrypt": "^5.1.1", + "cookie-parser": "^1.4.7", + "cors": "^2.8.5", + "dotenv": "^16.4.5", + "express": "^4.21.2", + "jsonwebtoken": "^9.0.2", + "multer": "^1.4.5-lts.1", + "socket": "^1.1.25", + "socket.io": "^4.8.1", + "superstruct": "^2.0.2", + "uuid": "^11.0.5" + } +} diff --git a/S_M8/prisma/migrations/20250111082621_/migration.sql b/S_M8/prisma/migrations/20250111082621_/migration.sql new file mode 100644 index 000000000..fcdea0ea9 --- /dev/null +++ b/S_M8/prisma/migrations/20250111082621_/migration.sql @@ -0,0 +1,43 @@ +-- CreateTable +CREATE TABLE "Article" ( + "id" SERIAL NOT NULL, + "title" TEXT NOT NULL, + "content" TEXT NOT NULL, + "image" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "Article_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "Product" ( + "id" SERIAL NOT NULL, + "name" TEXT NOT NULL, + "description" TEXT NOT NULL, + "price" INTEGER NOT NULL, + "tags" TEXT[], + "images" TEXT[], + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "Product_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "Comment" ( + "id" SERIAL NOT NULL, + "content" TEXT NOT NULL, + "productId" INTEGER, + "articleId" INTEGER, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "Comment_pkey" PRIMARY KEY ("id") +); + +-- AddForeignKey +ALTER TABLE "Comment" ADD CONSTRAINT "Comment_productId_fkey" FOREIGN KEY ("productId") REFERENCES "Product"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "Comment" ADD CONSTRAINT "Comment_articleId_fkey" FOREIGN KEY ("articleId") REFERENCES "Article"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/S_M8/prisma/migrations/20250304075708_add_user/migration.sql b/S_M8/prisma/migrations/20250304075708_add_user/migration.sql new file mode 100644 index 000000000..ed2457f77 --- /dev/null +++ b/S_M8/prisma/migrations/20250304075708_add_user/migration.sql @@ -0,0 +1,15 @@ +-- CreateTable +CREATE TABLE "User" ( + "id" SERIAL NOT NULL, + "email" TEXT NOT NULL, + "nickname" TEXT NOT NULL, + "image" TEXT, + "password" TEXT NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "User_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE UNIQUE INDEX "User_email_key" ON "User"("email"); diff --git a/S_M8/prisma/migrations/20250304082334_authorization/migration.sql b/S_M8/prisma/migrations/20250304082334_authorization/migration.sql new file mode 100644 index 000000000..9e47895b8 --- /dev/null +++ b/S_M8/prisma/migrations/20250304082334_authorization/migration.sql @@ -0,0 +1,31 @@ +/* + Warnings: + + - Added the required column `userId` to the `Article` table without a default value. This is not possible if the table is not empty. + - Added the required column `userId` to the `Comment` table without a default value. This is not possible if the table is not empty. + - Added the required column `userId` to the `Product` table without a default value. This is not possible if the table is not empty. + +*/ + +-- Delete ALL Articles, Comments, Products +DELETE FROM "Article"; +DELETE FROM "Comment"; +DELETE FROM "Product"; + +-- AlterTable +ALTER TABLE "Article" ADD COLUMN "userId" INTEGER NOT NULL; + +-- AlterTable +ALTER TABLE "Comment" ADD COLUMN "userId" INTEGER NOT NULL; + +-- AlterTable +ALTER TABLE "Product" ADD COLUMN "userId" INTEGER NOT NULL; + +-- AddForeignKey +ALTER TABLE "Article" ADD CONSTRAINT "Article_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "Product" ADD CONSTRAINT "Product_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "Comment" ADD CONSTRAINT "Comment_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE; diff --git a/S_M8/prisma/migrations/20250304094902_add_favorite_like/migration.sql b/S_M8/prisma/migrations/20250304094902_add_favorite_like/migration.sql new file mode 100644 index 000000000..918a2e4b2 --- /dev/null +++ b/S_M8/prisma/migrations/20250304094902_add_favorite_like/migration.sql @@ -0,0 +1,33 @@ +-- CreateTable +CREATE TABLE "Favorite" ( + "id" SERIAL NOT NULL, + "productId" INTEGER NOT NULL, + "userId" INTEGER NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "Favorite_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "Like" ( + "id" SERIAL NOT NULL, + "articleId" INTEGER NOT NULL, + "userId" INTEGER NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "Like_pkey" PRIMARY KEY ("id") +); + +-- AddForeignKey +ALTER TABLE "Favorite" ADD CONSTRAINT "Favorite_productId_fkey" FOREIGN KEY ("productId") REFERENCES "Product"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "Favorite" ADD CONSTRAINT "Favorite_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "Like" ADD CONSTRAINT "Like_articleId_fkey" FOREIGN KEY ("articleId") REFERENCES "Article"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "Like" ADD CONSTRAINT "Like_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE; diff --git a/S_M8/prisma/migrations/migration_lock.toml b/S_M8/prisma/migrations/migration_lock.toml new file mode 100644 index 000000000..fbffa92c2 --- /dev/null +++ b/S_M8/prisma/migrations/migration_lock.toml @@ -0,0 +1,3 @@ +# Please do not edit this file manually +# It should be added in your version-control system (i.e. Git) +provider = "postgresql" \ No newline at end of file diff --git a/S_M8/prisma/schema.prisma b/S_M8/prisma/schema.prisma new file mode 100644 index 000000000..66efc9922 --- /dev/null +++ b/S_M8/prisma/schema.prisma @@ -0,0 +1,102 @@ +// This is your Prisma schema file, +// learn more about it in the docs: https://pris.ly/d/prisma-schema + +// Looking for ways to speed up your queries, or scale easily with your serverless or edge functions? +// Try Prisma Accelerate: https://pris.ly/cli/accelerate-init + +generator client { + provider = "prisma-client-js" +} + +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") +} + +model Article { + id Int @id @default(autoincrement()) + title String + content String + image String? + comments Comment[] + user User @relation(fields: [userId], references: [id]) + userId Int + likes Like[] + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} + +model Product { + id Int @id @default(autoincrement()) + name String + description String + price Int + tags String[] + images String[] + comments Comment[] + user User @relation(fields: [userId], references: [id]) + userId Int + favorites Favorite[] + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} + +model Comment { + id Int @id @default(autoincrement()) + content String + product Product? @relation(fields: [productId], references: [id], onDelete: Cascade) + productId Int? + article Article? @relation(fields: [articleId], references: [id], onDelete: Cascade) + articleId Int? + user User @relation(fields: [userId], references: [id]) + userId Int + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} + +model User { + id Int @id @default(autoincrement()) + email String @unique + nickname String + image String? + password String + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + articles Article[] + products Product[] + comments Comment[] + favorites Favorite[] + likes Like[] + notifications Notification[] +} + +model Favorite { + id Int @id @default(autoincrement()) + product Product @relation(fields: [productId], references: [id]) + productId Int + user User @relation(fields: [userId], references: [id]) + userId Int + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} + +model Like { + id Int @id @default(autoincrement()) + article Article @relation(fields: [articleId], references: [id]) + articleId Int + user User @relation(fields: [userId], references: [id]) + userId Int + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} + +model Notification { +id Int @id @default(autoincrement()) +user User @relation(fields: [userId], references: [id]) +userId Int +type String +message String +payload Json? +isRead Boolean @default(false) +createdAt DateTime @default(now()) +} \ No newline at end of file diff --git a/S_M8/src/controllers/articlesController.ts b/S_M8/src/controllers/articlesController.ts new file mode 100644 index 000000000..84339f2e3 --- /dev/null +++ b/S_M8/src/controllers/articlesController.ts @@ -0,0 +1,79 @@ +import { Request, Response } from 'express'; +import { create } from 'superstruct'; +import { IdParamsStruct } from '../structs/commonStructs'; +import { + CreateArticleBodyStruct, + UpdateArticleBodyStruct, + GetArticleListParamsStruct, +} from '../structs/articlesStructs'; +import { CreateCommentBodyStruct, GetCommentListParamsStruct } from '../structs/commentsStruct'; +import * as articlesService from '../services/articlesService'; +import * as commentsService from '../services/commentsService'; +import * as likesService from '../services/likesService'; + +export async function createArticle(req: Request, res: Response) { + const data = create(req.body, CreateArticleBodyStruct); + const article = await articlesService.createArticle({ + ...data, + userId: req.user.id, + }); + res.status(201).send(article); +} + +export async function getArticle(req: Request, res: Response) { + const { id } = create(req.params, IdParamsStruct); + const article = await articlesService.getArticle(id); + res.send(article); +} + +export async function updateArticle(req: Request, res: Response) { + const { id } = create(req.params, IdParamsStruct); + const data = create(req.body, UpdateArticleBodyStruct); + const updatedArticle = await articlesService.updateArticle(id, { + ...data, + userId: req.user.id, + }); + res.send(updatedArticle); +} + +export async function deleteArticle(req: Request, res: Response) { + const { id } = create(req.params, IdParamsStruct); + await articlesService.deleteArticle(id, req.user.id); + res.status(204).send(); +} + +export async function getArticleList(req: Request, res: Response) { + const params = create(req.query, GetArticleListParamsStruct); + const result = await articlesService.getArticleList(params); + res.send(result); +} + +export async function createComment(req: Request, res: Response) { + const { id: articleId } = create(req.params, IdParamsStruct); + const { content } = create(req.body, CreateCommentBodyStruct); + const createdComment = await commentsService.createComment({ + articleId, + content, + userId: req.user.id, + }); + res.status(201).send(createdComment); +} + +export async function getCommentList(req: Request, res: Response) { + const { id: articleId } = create(req.params, IdParamsStruct); + const { cursor, limit } = create(req.query, GetCommentListParamsStruct); + const result = await commentsService.getCommentListByArticleId(articleId, { cursor, limit }); + res.send(result); +} + +export async function createLike(req: Request, res: Response) { + const { id: articleId } = create(req.params, IdParamsStruct); + await likesService.createLike(articleId, req.user.id); + res.status(201).send(); +} + +export async function deleteLike(req: Request, res: Response) { + const { id: articleId } = create(req.params, IdParamsStruct); + await likesService.deleteLike(articleId, req.user.id); + res.status(204).send(); +} diff --git a/S_M8/src/controllers/authController.ts b/S_M8/src/controllers/authController.ts new file mode 100644 index 000000000..ed29f511f --- /dev/null +++ b/S_M8/src/controllers/authController.ts @@ -0,0 +1,50 @@ +import { Request, Response } from 'express'; +import { create } from 'superstruct'; +import { ACCESS_TOKEN_COOKIE_NAME, REFRESH_TOKEN_COOKIE_NAME, NODE_ENV } from '../lib/constants'; +import { LoginBodyStruct, RegisterBodyStruct } from '../structs/authStructs'; +import * as authService from '../services/authService'; +import userResponseDTO from '../dto/userResponseDTO'; + +export async function register(req: Request, res: Response) { + const data = create(req.body, RegisterBodyStruct); + const user = await authService.register(data); + res.status(201).json(userResponseDTO(user)); +} + +export async function login(req: Request, res: Response) { + const data = create(req.body, LoginBodyStruct); + const { accessToken, refreshToken } = await authService.login(data); + setTokenCookies(res, accessToken, refreshToken); + res.status(200).send(); +} + +export async function logout(req: Request, res: Response) { + clearTokenCookies(res); + res.status(200).send(); +} + +export async function refreshToken(req: Request, res: Response) { + const refreshToken = req.cookies[REFRESH_TOKEN_COOKIE_NAME]; + const { accessToken, refreshToken: newRefreshToken } = await authService.refreshToken(refreshToken); + setTokenCookies(res, accessToken, newRefreshToken); + res.status(200).send(); +} + +function setTokenCookies(res: Response, accessToken: string, refreshToken: string) { + res.cookie(ACCESS_TOKEN_COOKIE_NAME, accessToken, { + httpOnly: true, + secure: NODE_ENV === 'production', + maxAge: 1 * 60 * 60 * 1000, // 1 hour + }); + res.cookie(REFRESH_TOKEN_COOKIE_NAME, refreshToken, { + httpOnly: true, + secure: NODE_ENV === 'production', + maxAge: 7 * 24 * 60 * 60 * 1000, // 7 days + path: '/auth/refresh', + }); +} + +function clearTokenCookies(res: Response) { + res.clearCookie(ACCESS_TOKEN_COOKIE_NAME); + res.clearCookie(REFRESH_TOKEN_COOKIE_NAME); +} diff --git a/S_M8/src/controllers/commentsController.ts b/S_M8/src/controllers/commentsController.ts new file mode 100644 index 000000000..3551cc97a --- /dev/null +++ b/S_M8/src/controllers/commentsController.ts @@ -0,0 +1,18 @@ +import { Request, Response } from 'express'; +import { create } from 'superstruct'; +import { UpdateCommentBodyStruct } from '../structs/commentsStruct'; +import { IdParamsStruct } from '../structs/commonStructs'; +import * as commentsService from '../services/commentsService'; + +export async function updateComment(req: Request, res: Response) { + const { id } = create(req.params, IdParamsStruct); + const { content } = create(req.body, UpdateCommentBodyStruct); + const updatedComment = await commentsService.updateComment(id, req.user.id, content); + res.send(updatedComment); +} + +export async function deleteComment(req: Request, res: Response) { + const { id } = create(req.params, IdParamsStruct); + await commentsService.deleteComment(id, req.user.id); + res.status(204).send(); +} diff --git a/S_M8/src/controllers/errorController.ts b/S_M8/src/controllers/errorController.ts new file mode 100644 index 000000000..c3a9fd784 --- /dev/null +++ b/S_M8/src/controllers/errorController.ts @@ -0,0 +1,50 @@ +import { Request, Response, NextFunction } from 'express'; +import { StructError } from 'superstruct'; +import BadRequestError from '../lib/errors/BadRequestError'; +import NotFoundError from '../lib/errors/NotFoundError'; +import UnauthorizedError from '../lib/errors/UnauthorizedError'; +import ForbiddenError from '../lib/errors/ForbiddenError'; + +export function defaultNotFoundHandler(req: Request, res: Response, next: NextFunction) { + res.status(404).send({ message: 'Not found' }); +} + +export function globalErrorHandler(err: Error, req: Request, res: Response, next: NextFunction) { + /** From superstruct or application error */ + if (err instanceof StructError || err instanceof BadRequestError) { + res.status(400).send({ message: err.message }); + return; + } + + /** From express.json middleware */ + if (err instanceof SyntaxError && 'body' in err) { + res.status(400).send({ message: 'Invalid JSON' }); + return; + } + + /** Prisma error codes */ + if ('code' in err) { + console.error(err); + res.status(500).send({ message: 'Failed to process data' }); + return; + } + + /** Application errors */ + if (err instanceof NotFoundError) { + res.status(404).send({ message: err.message }); + return; + } + + if (err instanceof UnauthorizedError) { + res.status(401).send({ message: err.message }); + return; + } + + if (err instanceof ForbiddenError) { + res.status(403).send({ message: err.message }); + return; + } + + console.error(err); + res.status(500).send({ message: 'Internal server error' }); +} diff --git a/S_M8/src/controllers/imagesController.ts b/S_M8/src/controllers/imagesController.ts new file mode 100644 index 000000000..421d5d942 --- /dev/null +++ b/S_M8/src/controllers/imagesController.ts @@ -0,0 +1,48 @@ +import { Request, Response } from 'express'; +import multer from 'multer'; +import path from 'path'; +import { v4 as uuidv4 } from 'uuid'; +import { PUBLIC_PATH, STATIC_PATH } from '../lib/constants'; +import BadRequestError from '../lib/errors/BadRequestError'; + +const ALLOWED_MIME_TYPES = ['image/png', 'image/jpeg', 'image/jpg']; +const FILE_SIZE_LIMIT = 5 * 1024 * 1024; + +export const upload = multer({ + storage: multer.diskStorage({ + destination(req, file, cb) { + cb(null, PUBLIC_PATH); + }, + filename(req, file, cb) { + const ext = path.extname(file.originalname); + const filename = `${uuidv4()}${ext}`; + cb(null, filename); + }, + }), + + limits: { + fileSize: FILE_SIZE_LIMIT, + }, + + fileFilter: function (req, file, cb) { + if (!ALLOWED_MIME_TYPES.includes(file.mimetype)) { + const err = new BadRequestError('Only png, jpeg, and jpg are allowed'); + return cb(err); + } + + cb(null, true); + }, +}); + +export async function uploadImage(req: Request, res: Response) { + const host = req.get('host'); + if (!host) { + throw new BadRequestError('Host is required'); + } + if (!req.file) { + throw new BadRequestError('File is required'); + } + const filePath = path.join(host, STATIC_PATH, req.file.filename); + const url = `http://${filePath}`; + res.send({ url }); +} diff --git a/S_M8/src/controllers/notification-controller.ts b/S_M8/src/controllers/notification-controller.ts new file mode 100644 index 000000000..e69de29bb diff --git a/S_M8/src/controllers/productsController.ts b/S_M8/src/controllers/productsController.ts new file mode 100644 index 000000000..a713a6c9f --- /dev/null +++ b/S_M8/src/controllers/productsController.ts @@ -0,0 +1,79 @@ +import { Request, Response } from 'express'; +import { create } from 'superstruct'; +import { IdParamsStruct } from '../structs/commonStructs'; +import { + CreateProductBodyStruct, + GetProductListParamsStruct, + UpdateProductBodyStruct, +} from '../structs/productsStruct'; +import { CreateCommentBodyStruct, GetCommentListParamsStruct } from '../structs/commentsStruct'; +import * as productsService from '../services/productsService'; +import * as commentsService from '../services/commentsService'; +import * as favoritesService from '../services/favoritesService'; + +export async function createProduct(req: Request, res: Response) { + const data = create(req.body, CreateProductBodyStruct); + const createdProduct = await productsService.createProduct({ + ...data, + userId: req.user.id, + }); + res.status(201).send(createdProduct); +} + +export async function getProduct(req: Request, res: Response) { + const { id } = create(req.params, IdParamsStruct); + const product = await productsService.getProduct(id); + res.send(product); +} + +export async function updateProduct(req: Request, res: Response) { + const { id } = create(req.params, IdParamsStruct); + const data = create(req.body, UpdateProductBodyStruct); + const updatedProduct = await productsService.updateProduct(id, { + ...data, + userId: req.user.id, + }); + res.send(updatedProduct); +} + +export async function deleteProduct(req: Request, res: Response) { + const { id } = create(req.params, IdParamsStruct); + await productsService.deleteProduct(id, req.user.id); + res.status(204).send(); +} + +export async function getProductList(req: Request, res: Response) { + const params = create(req.query, GetProductListParamsStruct); + const result = await productsService.getProductList(params, { + userId: req.user?.id, + }); + res.send(result); +} + +export async function createComment(req: Request, res: Response) { + const data = create(req.body, CreateCommentBodyStruct); + const createdComment = await commentsService.createComment({ + ...data, + userId: req.user.id, + }); + res.status(201).send(createdComment); +} + +export async function getCommentList(req: Request, res: Response) { + const { id: productId } = create(req.params, IdParamsStruct); + const params = create(req.query, GetCommentListParamsStruct); + const result = await commentsService.getCommentListByProductId(productId, params); + res.send(result); +} + +export async function createFavorite(req: Request, res: Response) { + const { id: productId } = create(req.params, IdParamsStruct); + await favoritesService.createFavorite(productId, req.user.id); + res.status(201).send(); +} + +export async function deleteFavorite(req: Request, res: Response) { + const { id: productId } = create(req.params, IdParamsStruct); + await favoritesService.deleteFavorite(productId, req.user.id); + res.status(204).send(); +} diff --git a/S_M8/src/controllers/usersController.ts b/S_M8/src/controllers/usersController.ts new file mode 100644 index 000000000..b9d1186eb --- /dev/null +++ b/S_M8/src/controllers/usersController.ts @@ -0,0 +1,58 @@ +import { Request, Response } from 'express'; +import { create } from 'superstruct'; +import { + UpdateMeBodyStruct, + UpdatePasswordBodyStruct, + GetMyProductListParamsStruct, + GetMyFavoriteListParamsStruct, +} from '../structs/usersStructs'; +import * as usersService from '../services/usersService'; +import * as authService from '../services/authService'; +import userResponseDTO from '../dto/userResponseDTO'; + +export async function getMe(req: Request, res: Response) { + const user = await usersService.getUser(req.user.id); + res.send(userResponseDTO(user)); +} + +export async function updateMe(req: Request, res: Response) { + const data = create(req.body, UpdateMeBodyStruct); + const updatedUser = await usersService.updateUser(req.user.id, data); + res.status(200).send(userResponseDTO(updatedUser)); +} + +export async function updateMyPassword(req: Request, res: Response) { + const { password, newPassword } = create(req.body, UpdatePasswordBodyStruct); + await authService.updateMyPassword(req.user.id, password, newPassword); + res.status(200).send(); +} + +export async function getMyProductList(req: Request, res: Response) { + const { page, pageSize, orderBy, keyword } = create(req.query, GetMyProductListParamsStruct); + const { list, totalCount } = await usersService.getMyProductList(req.user.id, { + page, + pageSize, + orderBy, + keyword, + }); + + res.send({ + list, + totalCount, + }); +} + +export async function getMyFavoriteList(req: Request, res: Response) { + const { page, pageSize, orderBy, keyword } = create(req.query, GetMyFavoriteListParamsStruct); + const { list, totalCount } = await usersService.getMyFavoriteList(req.user.id, { + page, + pageSize, + orderBy, + keyword, + }); + + res.send({ + list, + totalCount, + }); +} diff --git a/S_M8/src/dto/userResponseDTO.ts b/S_M8/src/dto/userResponseDTO.ts new file mode 100644 index 000000000..b5c6f8ad0 --- /dev/null +++ b/S_M8/src/dto/userResponseDTO.ts @@ -0,0 +1,8 @@ +import User from '../types/User'; + +const userResponseDTO = (user: User) => { + const { password, ...userWithoutPassword } = user; + return userWithoutPassword; +}; + +export default userResponseDTO; diff --git a/S_M8/src/lib/constants.ts b/S_M8/src/lib/constants.ts new file mode 100644 index 000000000..5f42b0ce4 --- /dev/null +++ b/S_M8/src/lib/constants.ts @@ -0,0 +1,12 @@ +import dotenv from 'dotenv'; +dotenv.config(); + +export const ACCESS_TOKEN_COOKIE_NAME = 'access-token'; +export const REFRESH_TOKEN_COOKIE_NAME = 'refresh-token'; +export const DATABASE_URL = process.env.DATABASE_URL || ''; +export const JWT_ACCESS_TOKEN_SECRET = process.env.JWT_ACCESS_TOKEN_SECRET || ''; +export const JWT_REFRESH_TOKEN_SECRET = process.env.JWT_REFRESH_TOKEN_SECRET || ''; +export const NODE_ENV = process.env.NODE_ENV || 'development'; +export const PORT = process.env.PORT || 3000; +export const PUBLIC_PATH = './public'; +export const STATIC_PATH = '/public'; diff --git a/S_M8/src/lib/errors/BadRequestError.ts b/S_M8/src/lib/errors/BadRequestError.ts new file mode 100644 index 000000000..4bafd09a6 --- /dev/null +++ b/S_M8/src/lib/errors/BadRequestError.ts @@ -0,0 +1,8 @@ +class BadRequestError extends Error { + constructor(message: string) { + super(message); + this.name = 'BadRequestError'; + } +} + +export default BadRequestError; diff --git a/S_M8/src/lib/errors/ForbiddenError.ts b/S_M8/src/lib/errors/ForbiddenError.ts new file mode 100644 index 000000000..6091711c1 --- /dev/null +++ b/S_M8/src/lib/errors/ForbiddenError.ts @@ -0,0 +1,8 @@ +class ForbiddenError extends Error { + constructor(message: string) { + super(message); + this.name = 'ForbiddenError'; + } +} + +export default ForbiddenError; diff --git a/S_M8/src/lib/errors/NotFoundError.ts b/S_M8/src/lib/errors/NotFoundError.ts new file mode 100644 index 000000000..cf993d9a8 --- /dev/null +++ b/S_M8/src/lib/errors/NotFoundError.ts @@ -0,0 +1,8 @@ +class NotFoundError extends Error { + constructor(modelName: string, id: number) { + super(`${modelName} with id ${id} not found`); + this.name = 'NotFoundError'; + } +} + +export default NotFoundError; diff --git a/S_M8/src/lib/errors/UnauthorizedError.ts b/S_M8/src/lib/errors/UnauthorizedError.ts new file mode 100644 index 000000000..980e8be4f --- /dev/null +++ b/S_M8/src/lib/errors/UnauthorizedError.ts @@ -0,0 +1,8 @@ +class UnauthorizedError extends Error { + constructor(message: string) { + super(message); + this.name = 'UnauthorizedError'; + } +} + +export default UnauthorizedError; diff --git a/S_M8/src/lib/prismaClient.ts b/S_M8/src/lib/prismaClient.ts new file mode 100644 index 000000000..5e41d6fe7 --- /dev/null +++ b/S_M8/src/lib/prismaClient.ts @@ -0,0 +1,3 @@ +import { PrismaClient } from '@prisma/client'; + +export const prismaClient = new PrismaClient(); diff --git a/S_M8/src/lib/socket.ts b/S_M8/src/lib/socket.ts new file mode 100644 index 000000000..e69de29bb diff --git a/S_M8/src/lib/token.ts b/S_M8/src/lib/token.ts new file mode 100644 index 000000000..086aa919a --- /dev/null +++ b/S_M8/src/lib/token.ts @@ -0,0 +1,28 @@ +import jwt from 'jsonwebtoken'; +import { JWT_ACCESS_TOKEN_SECRET, JWT_REFRESH_TOKEN_SECRET } from './constants'; + +export function generateTokens(userId: number) { + const accessToken = jwt.sign({ id: userId }, JWT_ACCESS_TOKEN_SECRET, { + expiresIn: '1h', + }); + const refreshToken = jwt.sign({ id: userId }, JWT_REFRESH_TOKEN_SECRET, { + expiresIn: '7d', + }); + return { accessToken, refreshToken }; +} + +export function verifyAccessToken(token: string) { + const decoded = jwt.verify(token, JWT_ACCESS_TOKEN_SECRET); + if (typeof decoded === 'string') { + throw new Error('Invalid token'); + } + return { userId: decoded.id }; +} + +export function verifyRefreshToken(token: string) { + const decoded = jwt.verify(token, JWT_REFRESH_TOKEN_SECRET); + if (typeof decoded === 'string') { + throw new Error('Invalid token'); + } + return { userId: decoded.id }; +} diff --git a/S_M8/src/lib/withAsync.ts b/S_M8/src/lib/withAsync.ts new file mode 100644 index 000000000..624a25b76 --- /dev/null +++ b/S_M8/src/lib/withAsync.ts @@ -0,0 +1,11 @@ +import { Request, Response, NextFunction, RequestHandler } from 'express'; + +export function withAsync(handler: RequestHandler) { + return async function (req: Request, res: Response, next: NextFunction) { + try { + await handler(req, res, next); + } catch (e) { + next(e); + } + }; +} diff --git a/S_M8/src/main.ts b/S_M8/src/main.ts new file mode 100644 index 000000000..e01746ad6 --- /dev/null +++ b/S_M8/src/main.ts @@ -0,0 +1,33 @@ +import express from 'express'; +import cors from 'cors'; +import path from 'path'; +import cookieParser from 'cookie-parser'; +import { PORT, PUBLIC_PATH, STATIC_PATH } from './lib/constants'; +import articlesRouter from './routers/articlesRouter'; +import productsRouter from './routers/productsRouter'; +import commentsRouter from './routers/commentsRouter'; +import imagesRouter from './routers/imagesRouter'; +import authRouter from './routers/authRouter'; +import usersRouter from './routers/usersRouter'; +import { defaultNotFoundHandler, globalErrorHandler } from './controllers/errorController'; + +const app = express(); + +app.use(cors()); +app.use(express.json()); +app.use(cookieParser()); +app.use(STATIC_PATH, express.static(path.resolve(process.cwd(), PUBLIC_PATH))); + +app.use('/articles', articlesRouter); +app.use('/products', productsRouter); +app.use('/comments', commentsRouter); +app.use('/images', imagesRouter); +app.use('/auth', authRouter); +app.use('/users', usersRouter); + +app.use(defaultNotFoundHandler); +app.use(globalErrorHandler); + +app.listen(PORT, () => { + console.log(`Server started on port ${PORT}`); +}); diff --git a/S_M8/src/middlewares/authenticate.ts b/S_M8/src/middlewares/authenticate.ts new file mode 100644 index 000000000..09d81725a --- /dev/null +++ b/S_M8/src/middlewares/authenticate.ts @@ -0,0 +1,23 @@ +import { Request, Response, NextFunction, RequestHandler } from 'express'; +import { ACCESS_TOKEN_COOKIE_NAME } from '../lib/constants'; +import * as authService from '../services/authService'; + +function authenticate(options = { optional: false }): RequestHandler { + return async (req: Request, res: Response, next: NextFunction) => { + const accessToken = req.cookies[ACCESS_TOKEN_COOKIE_NAME]; + try { + const user = await authService.authenticate(accessToken); + req.user = user; + } catch (error) { + if (options.optional) { + next(); + return; + } + next(error); + return; + } + next(); + }; +} + +export default authenticate; diff --git a/S_M8/src/repositories/articlesRepository.ts b/S_M8/src/repositories/articlesRepository.ts new file mode 100644 index 000000000..3de0e3e7c --- /dev/null +++ b/S_M8/src/repositories/articlesRepository.ts @@ -0,0 +1,94 @@ +import { Article } from '@prisma/client'; +import { prismaClient } from '../lib/prismaClient'; +import { PagePaginationParams } from '../types/pagination'; + +export async function createArticle(data: Omit) { + const createdArticle = await prismaClient.article.create({ + data, + }); + return createdArticle; +} + +export async function getArticle(id: number) { + const article = await prismaClient.article.findUnique({ where: { id } }); + return article; +} + +export async function getArticleWithLkes(id: number, { userId }: { userId?: number } = {}) { + const article = await prismaClient.article.findUnique({ + where: { id }, + include: { + likes: true, + }, + }); + + if (!article) { + return null; + } + + return { + ...article, + likes: undefined, + likeCount: article.likes.length, + isLiked: userId ? article.likes.some((like) => like.userId === userId) : undefined, + }; +} + +export async function getArticleListWithLikes( + { page, pageSize, orderBy, keyword }: PagePaginationParams, + { + userId, + }: { + userId?: number; + } = {}, +) { + const where = { + title: keyword ? { contains: keyword } : undefined, + }; + + const totalCount = await prismaClient.article.count({ where }); + const articles = await prismaClient.article.findMany({ + skip: (page - 1) * pageSize, + take: pageSize, + orderBy: orderBy === 'recent' ? { createdAt: 'desc' } : { id: 'asc' }, + where, + include: { + likes: true, + }, + }); + + const mappedArticles = articles.map((article) => ({ + ...article, + likes: undefined, + likeCount: article.likes.length, + isLiked: userId ? article.likes.some((like) => like.userId === userId) : undefined, + })); + + return { + list: mappedArticles, + totalCount, + }; +} + +export async function updateArticleWithLikes(id: number, data: Partial

) { + const updatedArticle = await prismaClient.article.update({ + where: { id }, + data, + include: { + likes: true, + }, + }); + return { + ...updatedArticle, + likeCount: updatedArticle.likes.length, + isLiked: data.userId + ? updatedArticle.likes.some((like) => like.userId === data.userId) + : undefined, + }; +} + +export async function deleteArticle(id: number) { + return prismaClient.article.delete({ + where: { id }, + }); +} diff --git a/S_M8/src/repositories/commentsRepository.ts b/S_M8/src/repositories/commentsRepository.ts new file mode 100644 index 000000000..d2a263b54 --- /dev/null +++ b/S_M8/src/repositories/commentsRepository.ts @@ -0,0 +1,50 @@ +import { Comment } from '@prisma/client'; +import { prismaClient } from '../lib/prismaClient'; +import { CursorPaginationParams } from '../types/pagination'; + +export async function createComment(data: Omit) { + const createdComment = await prismaClient.comment.create({ + data, + }); + return createdComment; +} + +export async function getComment(id: number) { + const comment = await prismaClient.comment.findUnique({ + where: { id }, + }); + return comment; +} + +export async function getCommentList( + where: { articleId?: number; productId?: number }, + { cursor, limit }: CursorPaginationParams, +) { + const commentsWithCursor = await prismaClient.comment.findMany({ + cursor: cursor ? { id: cursor } : undefined, + take: limit + 1, + where, + orderBy: { createdAt: 'desc' }, + }); + const comments = commentsWithCursor.slice(0, limit); + const cursorComment = commentsWithCursor[commentsWithCursor.length - 1]; + const nextCursor = cursorComment ? cursorComment.id : null; + + return { + list: comments, + nextCursor, + }; +} + +export async function updateComment(id: number, data: Partial) { + return prismaClient.comment.update({ + where: { id }, + data, + }); +} + +export async function deleteComment(id: number) { + return prismaClient.comment.delete({ + where: { id }, + }); +} diff --git a/S_M8/src/repositories/favoritesRepository.ts b/S_M8/src/repositories/favoritesRepository.ts new file mode 100644 index 000000000..6222994c6 --- /dev/null +++ b/S_M8/src/repositories/favoritesRepository.ts @@ -0,0 +1,22 @@ +import { Favorite } from '@prisma/client'; +import { prismaClient } from '../lib/prismaClient'; + +export async function createFavorite(data: Omit) { + const createdFavorite = await prismaClient.favorite.create({ + data, + }); + return createdFavorite; +} + +export async function getFavorite(productId: number, userId: number) { + const favorite = await prismaClient.favorite.findFirst({ + where: { productId, userId }, + }); + return favorite; +} + +export async function deleteFavorite(id: number) { + await prismaClient.favorite.delete({ + where: { id }, + }); +} diff --git a/S_M8/src/repositories/likesRepository.ts b/S_M8/src/repositories/likesRepository.ts new file mode 100644 index 000000000..b739ef439 --- /dev/null +++ b/S_M8/src/repositories/likesRepository.ts @@ -0,0 +1,22 @@ +import { Like } from '@prisma/client'; +import prismaClient from '../lib/prismaClient'; + +export async function createLike(data: Omit) { + const createdLike = await prismaClient.like.create({ + data, + }); + return createdLike; +} + +export async function getLike(articleId: number, userId: number) { + const like = await prismaClient.like.findFirst({ + where: { articleId, userId }, + }); + return like; +} + +export async function deleteLike(id: number) { + await prismaClient.like.delete({ + where: { id }, + }); +} diff --git a/S_M8/src/repositories/notification-repository.ts b/S_M8/src/repositories/notification-repository.ts new file mode 100644 index 000000000..3e21fbfd4 --- /dev/null +++ b/S_M8/src/repositories/notification-repository.ts @@ -0,0 +1,34 @@ +import prisma from "../lib/prismaClient"; + +export const notificationRepository = { + async create(data: { userId: number; type: string; message: string; payload?: any }) { + return await prisma.notification.create({ + data: { + userId: data.userId, + type: data.type, + message: data.message, + payload: data.payload ?? undefined, + }, + }); + }, + + async findByUser(userId: number) { + return await prisma.notification.findMany({ + where: { userId }, + orderBy: { createdAt: "desc" }, + }); + }, + + async countUnread(userId: number) { + return await prisma.notification.count({ + where: { userId, isRead: false }, + }); + }, + + async markAsRead(notificationId: number) { + return await prisma.notification.update({ + where: { id: notificationId }, + data: { isRead: true }, + }); + }, +}; diff --git a/S_M8/src/repositories/productsRepository.ts b/S_M8/src/repositories/productsRepository.ts new file mode 100644 index 000000000..315e820e6 --- /dev/null +++ b/S_M8/src/repositories/productsRepository.ts @@ -0,0 +1,154 @@ +import { Product } from '@prisma/client'; +import { prismaClient } from '../lib/prismaClient'; +import { PagePaginationParams } from '../types/pagination'; + +export async function createProduct(data: Omit) { + return prismaClient.product.create({ + data, + }); +} + +export async function getProduct(id: number) { + const product = await prismaClient.product.findUnique({ + where: { id }, + }); + return product; +} + +export async function getProductWithFavorites(id: number, userId?: number) { + const product = await prismaClient.product.findUnique({ + where: { id }, + include: { favorites: true }, + }); + if (!product) { + return null; + } + + const mappedProduct = { + ...product, + favorites: undefined, + favoriteCount: product.favorites.length, + isFavorited: userId + ? product.favorites.some((favorite) => favorite.userId === userId) + : undefined, + }; + return mappedProduct; +} + +export async function getProductListWithFavorites( + { page, pageSize, orderBy, keyword }: PagePaginationParams, + { + userId, + }: { + userId?: number; + } = {}, +) { + const where = keyword + ? { + OR: [{ name: { contains: keyword } }, { description: { contains: keyword } }], + } + : {}; + + const totalCount = await prismaClient.product.count({ + where, + }); + + const products = await prismaClient.product.findMany({ + skip: (page - 1) * pageSize, + take: pageSize, + orderBy: orderBy === 'recent' ? { id: 'desc' } : { id: 'asc' }, + where, + include: { + favorites: true, + }, + }); + + const mappedProducts = products.map((product) => ({ + ...product, + favorites: undefined, + favoriteCount: product.favorites.length, + isFavorited: + userId !== undefined + ? product.favorites.some((favorite) => favorite.userId === userId) + : undefined, + })); + + return { + list: mappedProducts, + totalCount, + }; +} + +export async function getFavoriteProductListByOwnerId( + ownerId: number, + { page, pageSize, orderBy, keyword }: PagePaginationParams, +) { + const where = keyword + ? { + OR: [{ name: { contains: keyword } }, { description: { contains: keyword } }], + } + : {}; + const totalCount = await prismaClient.product.count({ + where: { + ...where, + favorites: { + some: { + userId: ownerId, + }, + }, + }, + }); + const products = await prismaClient.product.findMany({ + skip: (page - 1) * pageSize, + take: pageSize, + orderBy: orderBy === 'recent' ? { id: 'desc' } : { id: 'asc' }, + where: { + ...where, + favorites: { + some: { + userId: ownerId, + }, + }, + }, + include: { + favorites: true, + }, + }); + + const mappedProducts = products.map((product) => ({ + ...product, + favorites: undefined, + favoriteCount: product.favorites.length, + isFavorited: true, + })); + + return { + list: mappedProducts, + totalCount, + }; +} + +export async function updateProductWithFavorites(id: number, data: Partial) { + const product = await prismaClient.product.update({ + where: { id }, + data, + include: { + favorites: true, + }, + }); + const mappedProduct = { + ...product, + favorites: undefined, + favoriteCount: product.favorites.length, + isFavorited: data.userId + ? product.favorites.some((favorite) => favorite.userId === data.userId) + : undefined, + }; + return mappedProduct; +} + +export async function deleteProduct(id: number) { + return prismaClient.product.delete({ + where: { id }, + }); +} diff --git a/S_M8/src/repositories/usersRepository.ts b/S_M8/src/repositories/usersRepository.ts new file mode 100644 index 000000000..c8a1cfa43 --- /dev/null +++ b/S_M8/src/repositories/usersRepository.ts @@ -0,0 +1,37 @@ +import { User } from '@prisma/client'; +import { prismaClient } from '../lib/prismaClient'; + +export async function createUser(data: Omit) { + const createdUser = await prismaClient.user.create({ + data, + }); + return createdUser; +} + +export async function getUser(id: number) { + const user = await prismaClient.user.findUnique({ + where: { id }, + }); + return user; +} + +export async function getUserByEmail(email: string) { + const user = await prismaClient.user.findUnique({ + where: { email }, + }); + return user; +} + +export async function updateUser(id: number, data: Partial) { + const updatedUser = await prismaClient.user.update({ + where: { id }, + data, + }); + return updatedUser; +} + +export async function deleteUser(id: number) { + await prismaClient.user.delete({ + where: { id }, + }); +} diff --git a/S_M8/src/routers/articlesRouter.ts b/S_M8/src/routers/articlesRouter.ts new file mode 100644 index 000000000..71c231543 --- /dev/null +++ b/S_M8/src/routers/articlesRouter.ts @@ -0,0 +1,28 @@ +import express from 'express'; +import { withAsync } from '../lib/withAsync'; +import { + createArticle, + getArticleList, + getArticle, + updateArticle, + deleteArticle, + createComment, + getCommentList, + createLike, + deleteLike, +} from '../controllers/articlesController'; +import authenticate from '../middlewares/authenticate'; + +const articlesRouter = express.Router(); + +articlesRouter.post('/', authenticate(), withAsync(createArticle)); +articlesRouter.get('/', authenticate({ optional: true }), withAsync(getArticleList)); +articlesRouter.get('/:id', authenticate({ optional: true }), withAsync(getArticle)); +articlesRouter.patch('/:id', authenticate(), withAsync(updateArticle)); +articlesRouter.delete('/:id', authenticate(), withAsync(deleteArticle)); +articlesRouter.post('/:id/comments', authenticate(), withAsync(createComment)); +articlesRouter.get('/:id/comments', withAsync(getCommentList)); +articlesRouter.post('/:id/likes', authenticate(), withAsync(createLike)); +articlesRouter.delete('/:id/likes', authenticate(), withAsync(deleteLike)); + +export default articlesRouter; diff --git a/S_M8/src/routers/authRouter.ts b/S_M8/src/routers/authRouter.ts new file mode 100644 index 000000000..f0650c297 --- /dev/null +++ b/S_M8/src/routers/authRouter.ts @@ -0,0 +1,12 @@ +import express from 'express'; +import { register, login, logout, refreshToken } from '../controllers/authController'; +import { withAsync } from '../lib/withAsync'; + +const authRouter = express.Router(); + +authRouter.post('/register', withAsync(register)); +authRouter.post('/login', withAsync(login)); +authRouter.post('/logout', withAsync(logout)); +authRouter.post('/refresh', withAsync(refreshToken)); + +export default authRouter; diff --git a/S_M8/src/routers/commentsRouter.ts b/S_M8/src/routers/commentsRouter.ts new file mode 100644 index 000000000..f0da623c4 --- /dev/null +++ b/S_M8/src/routers/commentsRouter.ts @@ -0,0 +1,11 @@ +import express from 'express'; +import { withAsync } from '../lib/withAsync'; +import { updateComment, deleteComment } from '../controllers/commentsController'; +import authenticate from '../middlewares/authenticate'; + +const commentsRouter = express.Router(); + +commentsRouter.patch('/:id', authenticate(), withAsync(updateComment)); +commentsRouter.delete('/:id', authenticate(), withAsync(deleteComment)); + +export default commentsRouter; diff --git a/S_M8/src/routers/imagesRouter.ts b/S_M8/src/routers/imagesRouter.ts new file mode 100644 index 000000000..9cd859445 --- /dev/null +++ b/S_M8/src/routers/imagesRouter.ts @@ -0,0 +1,9 @@ +import express from 'express'; +import { withAsync } from '../lib/withAsync'; +import { upload, uploadImage } from '../controllers/imagesController'; + +const imagesRouter = express.Router(); + +imagesRouter.post('/upload', upload.single('image'), withAsync(uploadImage)); + +export default imagesRouter; diff --git a/S_M8/src/routers/notification-router.ts b/S_M8/src/routers/notification-router.ts new file mode 100644 index 000000000..13b07c392 --- /dev/null +++ b/S_M8/src/routers/notification-router.ts @@ -0,0 +1,12 @@ +import { Router } from "express"; +import { notificationController } from "../controllers/notification-controller"; +import auth from "../middlewares/authenticate"; + +const router = Router(); + + +router.get("/", auth(), notificationController.list); +router.get("/unread-count", auth(), notificationController.unreadCount); +router.patch("/:id/read", auth(), notificationController.markRead); + +export default router; diff --git a/S_M8/src/routers/productsRouter.ts b/S_M8/src/routers/productsRouter.ts new file mode 100644 index 000000000..bd16e662f --- /dev/null +++ b/S_M8/src/routers/productsRouter.ts @@ -0,0 +1,28 @@ +import express from 'express'; +import { withAsync } from '../lib/withAsync'; +import { + createProduct, + getProduct, + updateProduct, + deleteProduct, + getProductList, + createComment, + getCommentList, + createFavorite, + deleteFavorite, +} from '../controllers/productsController'; +import authenticate from '../middlewares/authenticate'; + +const productsRouter = express.Router(); + +productsRouter.post('/', authenticate(), withAsync(createProduct)); +productsRouter.get('/:id', authenticate({ optional: true }), withAsync(getProduct)); +productsRouter.patch('/:id', authenticate(), withAsync(updateProduct)); +productsRouter.delete('/:id', authenticate(), withAsync(deleteProduct)); +productsRouter.get('/', authenticate({ optional: true }), withAsync(getProductList)); +productsRouter.post('/:id/comments', authenticate(), withAsync(createComment)); +productsRouter.get('/:id/comments', withAsync(getCommentList)); +productsRouter.post('/:id/favorites', authenticate(), withAsync(createFavorite)); +productsRouter.delete('/:id/favorites', authenticate(), withAsync(deleteFavorite)); + +export default productsRouter; diff --git a/S_M8/src/routers/usersRouter.ts b/S_M8/src/routers/usersRouter.ts new file mode 100644 index 000000000..a237d8693 --- /dev/null +++ b/S_M8/src/routers/usersRouter.ts @@ -0,0 +1,20 @@ +import express from 'express'; +import { withAsync } from '../lib/withAsync'; +import { + getMe, + updateMe, + updateMyPassword, + getMyProductList, + getMyFavoriteList, +} from '../controllers/usersController'; +import authenticate from '../middlewares/authenticate'; + +const usersRouter = express.Router(); + +usersRouter.get('/me', authenticate(), withAsync(getMe)); +usersRouter.patch('/me', authenticate(), withAsync(updateMe)); +usersRouter.patch('/me/password', authenticate(), withAsync(updateMyPassword)); +usersRouter.get('/me/products', authenticate(), withAsync(getMyProductList)); +usersRouter.get('/me/favorites', authenticate(), withAsync(getMyFavoriteList)); + +export default usersRouter; diff --git a/S_M8/src/services/articlesService.ts b/S_M8/src/services/articlesService.ts new file mode 100644 index 000000000..6e963bb78 --- /dev/null +++ b/S_M8/src/services/articlesService.ts @@ -0,0 +1,59 @@ +import * as articlesRepository from '../repositories/articlesRepository'; +import { PagePaginationParams, PagePaginationResult } from '../types/pagination'; +import ForbiddenError from '../lib/errors/ForbiddenError'; +import NotFoundError from '../lib/errors/NotFoundError'; +import Article from '../types/Article'; + +type CreateArticleData = Omit; +type UpdateArticleData = Partial & { userId: number }; + +export async function createArticle(data: CreateArticleData): Promise
{ + const createdArticle = await articlesRepository.createArticle(data); + return { + ...createdArticle, + likeCount: 0, + isLiked: false, + }; +} + +export async function getArticle(id: number): Promise
{ + const article = await articlesRepository.getArticleWithLkes(id); + if (!article) { + throw new NotFoundError('article', id); + } + return article; +} + +export async function getArticleList( + params: PagePaginationParams, +): Promise> { + const articles = await articlesRepository.getArticleListWithLikes(params); + return articles; +} + +export async function updateArticle(id: number, data: UpdateArticleData): Promise
{ + const existingArticle = await articlesRepository.getArticle(id); + if (!existingArticle) { + throw new NotFoundError('article', id); + } + + if (existingArticle.userId !== data.userId) { + throw new ForbiddenError('Should be the owner of the article'); + } + + const updatedArticle = await articlesRepository.updateArticleWithLikes(id, data); + return updatedArticle; +} + +export async function deleteArticle(id: number, userId: number): Promise { + const existingArticle = await articlesRepository.getArticle(id); + if (!existingArticle) { + throw new NotFoundError('article', id); + } + + if (existingArticle.userId !== userId) { + throw new ForbiddenError('Should be the owner of the article'); + } + + await articlesRepository.deleteArticle(id); +} diff --git a/S_M8/src/services/authService.ts b/S_M8/src/services/authService.ts new file mode 100644 index 000000000..4ab4944cc --- /dev/null +++ b/S_M8/src/services/authService.ts @@ -0,0 +1,102 @@ +import bcrypt from 'bcrypt'; +import * as usersRepository from '../repositories/usersRepository'; +import BadRequestError from '../lib/errors/BadRequestError'; +import NotFoundError from '../lib/errors/NotFoundError'; +import { generateTokens, verifyAccessToken, verifyRefreshToken } from '../lib/token'; +import UnauthorizedError from '../lib/errors/UnauthorizedError'; +import User from '../types/User'; + +type RegisterData = Omit; +type LoginData = Pick; + +async function verifyPassword(user: User, password: string) { + return await bcrypt.compare(password, user.password); +} + +async function hashPassword(password: string) { + const salt = await bcrypt.genSalt(10); + return await bcrypt.hash(password, salt); +} + +export async function register(data: RegisterData) { + const existingUser = await usersRepository.getUserByEmail(data.email); + if (existingUser) { + throw new BadRequestError('User already exists'); + } + + const hashedPassword = await hashPassword(data.password); + + const user = await usersRepository.createUser({ + email: data.email, + nickname: data.nickname, + password: hashedPassword, + image: data.image, + }); + + return user; +} + +export async function login(data: LoginData) { + const user = await usersRepository.getUserByEmail(data.email); + if (!user) { + throw new BadRequestError('Invalid credentials'); + } + + const isPasswordValid = await verifyPassword(user, data.password); + if (!isPasswordValid) { + throw new BadRequestError('Invalid credentials'); + } + + const { accessToken, refreshToken } = generateTokens(user.id); + return { + accessToken, + refreshToken, + }; +} + +export async function refreshToken(refreshToken?: string) { + if (!refreshToken) { + throw new BadRequestError('Invalid refresh token'); + } + + const { userId } = verifyRefreshToken(refreshToken); + + const user = await usersRepository.getUser(userId); + if (!user) { + throw new BadRequestError('Invalid refresh token'); + } + + const { accessToken, refreshToken: newRefreshToken } = generateTokens(userId); + return { + accessToken, + refreshToken: newRefreshToken, + }; +} + +export async function updateMyPassword(userId: User['id'], password: string, newPassword: string) { + const user = await usersRepository.getUser(userId); + if (!user) { + throw new NotFoundError('user', userId); + } + + const isPasswordValid = await verifyPassword(user, password); + if (!isPasswordValid) { + throw new BadRequestError('Invalid credentials'); + } + + const hashedPassword = await hashPassword(newPassword); + await usersRepository.updateUser(userId, { password: hashedPassword }); +} + +export async function authenticate(accessToken?: string) { + if (!accessToken) { + throw new UnauthorizedError('Unauthorized'); + } + + const { userId } = verifyAccessToken(accessToken); + const user = await usersRepository.getUser(userId); + if (!user) { + throw new UnauthorizedError('Unauthorized'); + } + return user; +} diff --git a/S_M8/src/services/commentsService.ts b/S_M8/src/services/commentsService.ts new file mode 100644 index 000000000..300f9fc7f --- /dev/null +++ b/S_M8/src/services/commentsService.ts @@ -0,0 +1,135 @@ +import * as articlesRepository from '../repositories/articlesRepository'; +import * as commentsRepository from '../repositories/commentsRepository'; +import * as productsRepository from '../repositories/productsRepository'; +import { CursorPaginationParams, CursorPaginationResult } from '../types/pagination'; +import BadRequestError from '../lib/errors/BadRequestError'; +import ForbiddenError from '../lib/errors/ForbiddenError'; +import NotFoundError from '../lib/errors/NotFoundError'; +import Comment from '../types/Comment'; +import { notificationService } from './notification-service'; + +type CreateCommentData = Omit< + Comment, + 'id' | 'productId' | 'articleId' | 'createdAt' | 'updatedAt' +> & { + productId?: number; + articleId?: number; +}; + +export async function createComment(data: CreateCommentData): Promise { + if (!data.articleId && !data.productId) { + throw new BadRequestError('Either articleId or productId must be provided'); + } + + if (data.articleId) { + const article = await articlesRepository.getArticle(data.articleId); + if (!article) { + throw new NotFoundError('article', data.articleId); + } + } + + if (data.productId) { + const product = await productsRepository.getProduct(data.productId); + if (!product) { + throw new NotFoundError('product', data.productId); + } + } + + const comment = await commentsRepository.createComment({ + ...data, + articleId: data.articleId ?? null, + productId: data.productId ?? null, + }); + + try { + if (data.articleId) { + const article = await articlesRepository.getArticle(data.articleId); + if (article && article.userId !== comment.userId) { + const message = `게시글 \"${article.title}\"에 새로운 댓글이 달렸습니다: \"${(comment.content || '').slice(0, 80)}\"`; + await notificationService.sendNotification( + article.userId, + 'post_comment', + message, + { articleId: article.id, commentId: comment.id } + ); + } + } + + if (data.productId) { + const product = await productsRepository.getProduct(data.productId); + if (product && product.userId !== comment.userId) { + const message = `상품 \"${product.name}\"에 새로운 댓글이 달렸습니다: \"${(comment.content || '').slice(0, 80)}\"`; + await notificationService.sendNotification( + product.userId, + 'product_comment', + message, + { productId: product.id, commentId: comment.id } + ); + } + } + } catch (err) { + console.warn('Failed to send comment notifications:', (err as Error).message); + } + + return comment; +} + +export async function getComment(id: number): Promise { + const comment = await commentsRepository.getComment(id); + if (!comment) { + throw new NotFoundError('comment', id); + } + return comment; +} + +export async function getCommentListByArticleId( + articleId: number, + params: CursorPaginationParams, +): Promise> { + const article = await articlesRepository.getArticle(articleId); + if (!article) { + throw new NotFoundError('article', articleId); + } + + const result = commentsRepository.getCommentList({ articleId }, params); + return result; +} + +export async function getCommentListByProductId( + productId: number, + params: CursorPaginationParams, +): Promise> { + const product = await productsRepository.getProduct(productId); + if (!product) { + throw new NotFoundError('product', productId); + } + + const result = commentsRepository.getCommentList({ productId }, params); + return result; +} + +export async function updateComment(id: number, userId: number, content: string): Promise { + const comment = await commentsRepository.getComment(id); + if (!comment) { + throw new NotFoundError('comment', id); + } + + if (comment.userId !== userId) { + throw new ForbiddenError('Should be the owner of the comment'); + } + + return commentsRepository.updateComment(id, { content }); +} + +export async function deleteComment(id: number, userId: number): Promise { + const comment = await commentsRepository.getComment(id); + if (!comment) { + throw new NotFoundError('comment', id); + } + + if (comment.userId !== userId) { + throw new ForbiddenError('Should be the owner of the comment'); + } + + await commentsRepository.deleteComment(id); +} diff --git a/S_M8/src/services/favoritesService.ts b/S_M8/src/services/favoritesService.ts new file mode 100644 index 000000000..373e21e2c --- /dev/null +++ b/S_M8/src/services/favoritesService.ts @@ -0,0 +1,32 @@ +import * as favoritesRepository from '../repositories/favoritesRepository'; +import * as productsRepository from '../repositories/productsRepository'; +import NotFoundError from '../lib/errors/NotFoundError'; +import BadRequestError from '../lib/errors/BadRequestError'; + +export async function createFavorite(productId: number, userId: number) { + const existingProduct = await productsRepository.getProduct(productId); + if (!existingProduct) { + throw new NotFoundError('product', productId); + } + + const existingFavorite = await favoritesRepository.getFavorite(productId, userId); + if (existingFavorite) { + throw new BadRequestError('Already favorited'); + } + + await favoritesRepository.createFavorite({ productId, userId }); +} + +export async function deleteFavorite(productId: number, userId: number) { + const existingProduct = await productsRepository.getProduct(productId); + if (!existingProduct) { + throw new NotFoundError('product', productId); + } + + const existingFavorite = await favoritesRepository.getFavorite(productId, userId); + if (!existingFavorite) { + throw new BadRequestError('Not favorited'); + } + + await favoritesRepository.deleteFavorite(existingFavorite.id); +} diff --git a/S_M8/src/services/likesService.ts b/S_M8/src/services/likesService.ts new file mode 100644 index 000000000..b15fdd70a --- /dev/null +++ b/S_M8/src/services/likesService.ts @@ -0,0 +1,32 @@ +import * as likesRepository from '../repositories/likesRepository'; +import * as articlesRepository from '../repositories/articlesRepository'; +import NotFoundError from '../lib/errors/NotFoundError'; +import BadRequestError from '../lib/errors/BadRequestError'; + +export async function createLike(articleId: number, userId: number) { + const existingArticle = await articlesRepository.getArticle(articleId); + if (!existingArticle) { + throw new NotFoundError('article', articleId); + } + + const existingLike = await likesRepository.getLike(articleId, userId); + if (existingLike) { + throw new BadRequestError('Already liked'); + } + + await likesRepository.createLike({ articleId, userId }); +} + +export async function deleteLike(articleId: number, userId: number) { + const existingArticle = await articlesRepository.getArticle(articleId); + if (!existingArticle) { + throw new NotFoundError('article', articleId); + } + + const existingLike = await likesRepository.getLike(articleId, userId); + if (!existingLike) { + throw new BadRequestError('Not liked'); + } + + await likesRepository.deleteLike(existingLike.id); +} diff --git a/S_M8/src/services/notification-service.ts b/S_M8/src/services/notification-service.ts new file mode 100644 index 000000000..e8c0e922f --- /dev/null +++ b/S_M8/src/services/notification-service.ts @@ -0,0 +1,39 @@ +import { notificationRepository } from "../repositories/notification-repository"; +import { getIO } from "../lib/socket"; +import prisma from "../lib/prismaClient"; +import { Prisma } from "@prisma/client"; + +export const notificationService = { + async getNotifications(userId: number) { + return await notificationRepository.findByUser(userId); + }, + + async getUnreadCount(userId: number) { + return await notificationRepository.countUnread(userId); + }, + + async markAsRead(notificationId: number) { + return await notificationRepository.markAsRead(notificationId); + }, + + async sendNotification( + userId: number, + type: string, + message: string, + payload?: Record + ) { + const notification = await prisma.notification.create({ + data: { + userId, + type, + message, + payload: payload ? payload : Prisma.JsonNull, + }, + }); + + const io = getIO(); + io.to(`user_${userId}`).emit("notification", notification); + + return notification; + }, +}; diff --git a/S_M8/src/services/productsService.ts b/S_M8/src/services/productsService.ts new file mode 100644 index 000000000..6ea39ddae --- /dev/null +++ b/S_M8/src/services/productsService.ts @@ -0,0 +1,59 @@ +import ForbiddenError from '../lib/errors/ForbiddenError'; +import NotFoundError from '../lib/errors/NotFoundError'; +import * as productsRepository from '../repositories/productsRepository'; +import { PagePaginationParams, PagePaginationResult } from '../types/pagination'; +import Product from '../types/Product'; + +type CreateProductData = Omit< + Product, + 'id' | 'createdAt' | 'updatedAt' | 'favoriteCount' | 'isFavorited' +>; +type UpdateProductData = Partial & { userId: number }; + +export async function createProduct(data: CreateProductData): Promise { + const createdProduct = await productsRepository.createProduct(data); + return { + ...createdProduct, + favoriteCount: 0, + isFavorited: false, + }; +} + +export async function getProduct(id: number): Promise { + const product = await productsRepository.getProductWithFavorites(id); + if (!product) { + throw new NotFoundError('product', id); + } + return product; +} + +export async function getProductList( + params: PagePaginationParams, + { userId }: { userId?: number } = {}, +): Promise> { + const products = await productsRepository.getProductListWithFavorites(params, { userId }); + return products; +} + +export async function updateProduct(id: number, data: UpdateProductData): Promise { + const existingProduct = await productsRepository.getProduct(id); + if (!existingProduct) { + throw new NotFoundError('product', id); + } + if (existingProduct.userId !== data.userId) { + throw new ForbiddenError('Should be the owner of the product'); + } + const updatedProduct = await productsRepository.updateProductWithFavorites(id, data); + return updatedProduct; +} + +export async function deleteProduct(id: number, userId: number): Promise { + const existingProduct = await productsRepository.getProduct(id); + if (!existingProduct) { + throw new NotFoundError('product', id); + } + if (existingProduct.userId !== userId) { + throw new ForbiddenError('Should be the owner of the product'); + } + await productsRepository.deleteProduct(id); +} diff --git a/S_M8/src/services/usersService.ts b/S_M8/src/services/usersService.ts new file mode 100644 index 000000000..4764a3154 --- /dev/null +++ b/S_M8/src/services/usersService.ts @@ -0,0 +1,38 @@ +import { Product } from '@prisma/client'; +import { PagePaginationParams, PagePaginationResult } from '../types/pagination'; +import * as usersRepository from '../repositories/usersRepository'; +import * as productsRepository from '../repositories/productsRepository'; +import NotFoundError from '../lib/errors/NotFoundError'; +import User from '../types/User'; + +type UpdateUserData = Partial>; + +export async function getUser(userId: number): Promise { + const user = await usersRepository.getUser(userId); + if (!user) { + throw new NotFoundError('user', userId); + } + + return user; +} + +export async function updateUser(userId: number, data: Partial): Promise { + const updatedUser = await usersRepository.updateUser(userId, data); + return updatedUser; +} + +export async function getMyProductList( + userId: number, + params: PagePaginationParams, +): Promise> { + const result = await productsRepository.getProductListWithFavorites(params, { userId }); + return result; +} + +export async function getMyFavoriteList( + userId: number, + params: PagePaginationParams, +): Promise> { + const result = await productsRepository.getFavoriteProductListByOwnerId(userId, params); + return result; +} diff --git a/S_M8/src/structs/articlesStructs.ts b/S_M8/src/structs/articlesStructs.ts new file mode 100644 index 000000000..3d25131b5 --- /dev/null +++ b/S_M8/src/structs/articlesStructs.ts @@ -0,0 +1,12 @@ +import { coerce, nonempty, nullable, object, partial, string } from 'superstruct'; +import { PageParamsStruct } from './commonStructs'; + +export const GetArticleListParamsStruct = PageParamsStruct; + +export const CreateArticleBodyStruct = object({ + title: coerce(nonempty(string()), string(), (value) => value.trim()), + content: nonempty(string()), + image: nullable(string()), +}); + +export const UpdateArticleBodyStruct = partial(CreateArticleBodyStruct); diff --git a/S_M8/src/structs/authStructs.ts b/S_M8/src/structs/authStructs.ts new file mode 100644 index 000000000..f8b6bcd60 --- /dev/null +++ b/S_M8/src/structs/authStructs.ts @@ -0,0 +1,14 @@ +import { nonempty, nullable, object, string } from 'superstruct'; + +export const RegisterBodyStruct = object({ + email: nonempty(string()), + nickname: nonempty(string()), + password: nonempty(string()), + image: nullable(string()), +}); + +export const LoginBodyStruct = object({ + email: nonempty(string()), + password: nonempty(string()), +}); + diff --git a/S_M8/src/structs/commentsStruct.ts b/S_M8/src/structs/commentsStruct.ts new file mode 100644 index 000000000..aff08b474 --- /dev/null +++ b/S_M8/src/structs/commentsStruct.ts @@ -0,0 +1,12 @@ +import { nonempty, number, object, optional, string } from 'superstruct'; +import { CursorParamsStruct } from './commonStructs'; + +export const CreateCommentBodyStruct = object({ + content: nonempty(string()), + productId: optional(number()), + articleId: optional(number()), +}); + +export const GetCommentListParamsStruct = CursorParamsStruct; + +export const UpdateCommentBodyStruct = CreateCommentBodyStruct; diff --git a/S_M8/src/structs/commonStructs.ts b/S_M8/src/structs/commonStructs.ts new file mode 100644 index 000000000..605bfa9e0 --- /dev/null +++ b/S_M8/src/structs/commonStructs.ts @@ -0,0 +1,22 @@ +import { coerce, integer, object, string, defaulted, optional, enums, nonempty } from 'superstruct'; + +/** Convert string to integer then validate it */ +const integerString = coerce(integer(), string(), (value) => parseInt(value)); + +export const IdParamsStruct = object({ + id: integerString, +}); + +export const PageParamsStruct = object({ + page: defaulted(integerString, 1), + pageSize: defaulted(integerString, 10), + orderBy: optional(enums(['recent'])), + keyword: optional(nonempty(string())), +}); + +export const CursorParamsStruct = object({ + cursor: defaulted(integerString, 0), + limit: defaulted(integerString, 10), + orderBy: optional(enums(['recent'])), + keyword: optional(nonempty(string())), +}); diff --git a/S_M8/src/structs/productsStruct.ts b/S_M8/src/structs/productsStruct.ts new file mode 100644 index 000000000..a548f57d5 --- /dev/null +++ b/S_M8/src/structs/productsStruct.ts @@ -0,0 +1,14 @@ +import { coerce, partial, object, string, min, nonempty, array, integer } from 'superstruct'; +import { PageParamsStruct } from './commonStructs'; + +export const CreateProductBodyStruct = object({ + name: coerce(nonempty(string()), string(), (value) => value.trim()), + description: nonempty(string()), + price: min(integer(), 0), + tags: array(nonempty(string())), + images: array(nonempty(string())), +}); + +export const GetProductListParamsStruct = PageParamsStruct; + +export const UpdateProductBodyStruct = partial(CreateProductBodyStruct); diff --git a/S_M8/src/structs/usersStructs.ts b/S_M8/src/structs/usersStructs.ts new file mode 100644 index 000000000..9a46e8beb --- /dev/null +++ b/S_M8/src/structs/usersStructs.ts @@ -0,0 +1,19 @@ +import { nullable, object, partial, string } from 'superstruct'; +import { PageParamsStruct } from './commonStructs'; + +export const UpdateMeBodyStruct = partial( + object({ + email: string(), + nickname: string(), + image: nullable(string()), + }), +); + +export const UpdatePasswordBodyStruct = object({ + password: string(), + newPassword: string(), +}); + +export const GetMyProductListParamsStruct = PageParamsStruct; + +export const GetMyFavoriteListParamsStruct = PageParamsStruct; diff --git a/S_M8/src/types/Article.ts b/S_M8/src/types/Article.ts new file mode 100644 index 000000000..815abfcda --- /dev/null +++ b/S_M8/src/types/Article.ts @@ -0,0 +1,13 @@ +interface Article { + id: number; + title: string; + content: string; + image: string | null; + userId: number; + createdAt: Date; + updatedAt: Date; + likeCount: number; + isLiked?: boolean; +} + +export default Article; diff --git a/S_M8/src/types/Comment.ts b/S_M8/src/types/Comment.ts new file mode 100644 index 000000000..d8d65d34e --- /dev/null +++ b/S_M8/src/types/Comment.ts @@ -0,0 +1,11 @@ +interface Comment { + id: number; + content: string; + userId: number; + articleId: number | null; + productId: number | null; + createdAt: Date; + updatedAt: Date; +} + +export default Comment; diff --git a/S_M8/src/types/Product.ts b/S_M8/src/types/Product.ts new file mode 100644 index 000000000..52c8aa64d --- /dev/null +++ b/S_M8/src/types/Product.ts @@ -0,0 +1,15 @@ +interface Product { + id: number; + name: string; + description: string; + price: number; + tags: string[]; + images: string[]; + userId: number; + createdAt: Date; + updatedAt: Date; + favoriteCount: number; + isFavorited?: boolean; +} + +export default Product; diff --git a/S_M8/src/types/User.ts b/S_M8/src/types/User.ts new file mode 100644 index 000000000..99ebe3a86 --- /dev/null +++ b/S_M8/src/types/User.ts @@ -0,0 +1,11 @@ +interface User { + id: number; + email: string; + password: string; + nickname: string; + image: string | null; + createdAt: Date; + updatedAt: Date; +} + +export default User; diff --git a/S_M8/src/types/express.d.ts b/S_M8/src/types/express.d.ts new file mode 100644 index 000000000..1fc9b1ac4 --- /dev/null +++ b/S_M8/src/types/express.d.ts @@ -0,0 +1,9 @@ +import Express from 'express'; + +declare global { + namespace Express { + interface Request { + user: User; + } + } +} diff --git a/S_M8/src/types/notification.ts b/S_M8/src/types/notification.ts new file mode 100644 index 000000000..2ddf174cb --- /dev/null +++ b/S_M8/src/types/notification.ts @@ -0,0 +1,9 @@ +export type NotificationDTO = { +id: number; +userId: number; +type: string; +message: string; +payload?: any; +isRead: boolean; +createdAt: string; +}; \ No newline at end of file diff --git a/S_M8/src/types/pagination.ts b/S_M8/src/types/pagination.ts new file mode 100644 index 000000000..05e4b2831 --- /dev/null +++ b/S_M8/src/types/pagination.ts @@ -0,0 +1,21 @@ +export interface PagePaginationParams { + page: number; + pageSize: number; + orderBy?: 'recent' | 'oldest'; + keyword?: string; +} + +export interface PagePaginationResult { + list: T[]; + totalCount: number; +} + +export interface CursorPaginationParams { + cursor: number; + limit: number; +} + +export interface CursorPaginationResult { + list: T[]; + nextCursor: number | null; +} diff --git a/S_M8/tests/test.http b/S_M8/tests/test.http new file mode 100644 index 000000000..89223976d --- /dev/null +++ b/S_M8/tests/test.http @@ -0,0 +1,188 @@ +### 모든 Article 가져오기 +GET http://localhost:3000/articles?orderBy=recent + +### 새로운 Article 만들기 +POST http://localhost:3000/articles +Content-Type: application/json + +{ + "title": "새로운 Article", + "content": "이것은 새로운 Article의 내용입니다.", + "image": "https://example.com/image.jpg" +} + +### 특정 ID의 Article 가져오기 +GET http://localhost:3000/articles/1 + +### Article 업데이트하기 +PATCH http://localhost:3000/articles/1 +Content-Type: application/json + +{ + "title": "Article 업데이트", + "content": "이것은 업데이트된 내용입니다." +} + +### Article 삭제하기 +DELETE http://localhost:3000/articles/1 + +### Article 좋아요 테스트 +POST http://localhost:3000/articles/1/likes + +### Article 좋아요 취소 테스트 +DELETE http://localhost:3000/articles/1/likes + + + +### Article에 Comment 달기 테스트 +POST http://localhost:3000/articles/1/comments +Content-Type: application/json + +{ + "content": "이것은 Article에 대한 테스트 Comment입니다." +} + +### Article의 Comment 가져오기 테스트 +GET http://localhost:3000/articles/1/comments?limit=4 + +### Comment 업데이트 테스트 +PATCH http://localhost:3000/comments/1 +Content-Type: application/json + +{ + "content": "이것은 업데이트된 테스트 Comment입니다." +} + +### Comment 삭제 테스트 +DELETE http://localhost:3000/comments/1 + +### 새로운 Product 만들기 테스트 +POST http://localhost:3000/products +Content-Type: application/json + +{ + "name": "테스트 Product", + "description": "이것은 테스트 Product 설명입니다.", + "price": 100, + "tags": [], + "images": ["image1.png", "image2.png"] +} + +### 모든 Product 가져오기 테스트 +GET http://localhost:3000/products + +### 특정 ID의 Product 가져오기 테스트 +GET http://localhost:3000/products/1 + +### Product 업데이트하기 테스트 +PATCH http://localhost:3000/products/2 +Content-Type: application/json + +{ + "name": "업데이트된 테스트 Product", + "description": "이것은 업데이트된 테스트 Product 설명입니다.", + "price": 150, + "tags": ["업데이트", "Product"], + "images": ["업데이트된_image1.png"] +} + +### Product 삭제하기 테스트 +DELETE http://localhost:3000/products/2 + +### Product 좋아요 테스트 +POST http://localhost:3000/products/1/favorites + +### Product 좋아요 취소 테스트 +DELETE http://localhost:3000/products/1/favorites + + +### Product에 Comment 달기 테스트 +POST http://localhost:3000/products/1/comments +Content-Type: application/json + +{ + "content": "이것은 Product에 대한 테스트 Comment입니다." +} + +### Product의 Comment 가져오기 테스트 +GET http://localhost:3000/products/1/comments?limit=5 + + +### Product의 Comment 가져오기 테스트 (Cursor) +GET http://localhost:3000/products/1/comments?cursor=1&limit=5 + + +### Comment 업데이트하기 테스트 +PATCH http://localhost:3000/comments/2 +Content-Type: application/json + +{ + "content": "이것은 업데이트된 테스트 Comment입니다." +} + +### Comment 삭제하기 테스트 +DELETE http://localhost:3000/comments/2 + +### 이미지 업로드 테스트 +POST http://localhost:3000/images/upload +Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW + +------WebKitFormBoundary7MA4YWxkTrZu0gW +Content-Disposition: form-data; name="image"; filename="test.png" +Content-Type: image/png + +< ./test.png +------WebKitFormBoundary7MA4YWxkTrZu0gW-- + +### 회원가입 테스트 +POST http://localhost:3000/auth/register +Content-Type: application/json + +{ + + "email": "testuser@example.com", + "nickname": "testuser", + "password": "testpassword" +} + +### 로그인 테스트 +POST http://localhost:3000/auth/login +Content-Type: application/json + +{ + "email": "testuser@example.com", + "password": "testpassword" +} + +### 로그아웃 테스트 +POST http://localhost:3000/auth/logout + +### 토큰 갱신 테스트 +POST http://localhost:3000/auth/refresh + +### 내 정보 가져오기 테스트 +GET http://localhost:3000/users/me + +### 내 정보 업데이트 테스트 +PATCH http://localhost:3000/users/me +Content-Type: application/json + +{ + "nickname": "testuser-edit" +} + +### 내 비밀번호 업데이트 테스트 +PATCH http://localhost:3000/users/me/password +Content-Type: application/json + +{ + "password": "testpassword", + "newPassword": "newtestpassword" +} + +### 내 상품 목록 가져오기 테스트 +GET http://localhost:3000/users/me/products?page=1&pageSize=5 + +### 내 좋아요 목록 가져오기 테스트 +GET http://localhost:3000/users/me/favorites?page=1&pageSize=5 + diff --git a/S_M8/tests/test.js b/S_M8/tests/test.js new file mode 100644 index 000000000..8cd82dc8c --- /dev/null +++ b/S_M8/tests/test.js @@ -0,0 +1,68 @@ +async function bulkArticleCreate() { + for (let i = 1; i <= 100; i++) { + await fetch(`http://localhost:3000/articles`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + title: `Article ${i}`, + content: `This is the content of article ${i}.`, + image: `https://example.com/image${i}.jpg`, + }), + }); + } +} + +async function bulkArticleCommentCreate(articleId) { + for (let i = 1; i <= 100; i++) { + await fetch(`http://localhost:3000/articles/${articleId}/comments`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + content: `This is the content of article ${articleId} comment${i}.`, + }), + }); + } +} + +async function bulkProductCreate() { + for (let i = 1; i <= 100; i++) { + await fetch(`http://localhost:3000/products`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + name: `Test Product ${i}`, + description: `This is a test product description ${i}.`, + price: 100, + tags: ['test', 'product'], + images: ['image1.png', 'image2.png'], + }), + }); + } +} + +async function bulkProductCommentCreate(productId) { + for (let i = 1; i <= 100; i++) { + await fetch(`http://localhost:3000/products/${productId}/comments`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + content: `This is the content of product ${productId} comment${i}.`, + }), + }); + } +} + +bulkArticleCreate(); +bulkArticleCommentCreate(1); +bulkArticleCommentCreate(2); +bulkProductCreate(); +bulkProductCommentCreate(1); +bulkProductCommentCreate(2); diff --git a/S_M8/tests/test.png b/S_M8/tests/test.png new file mode 100644 index 0000000000000000000000000000000000000000..35a7b5616a6b18773cb64d62627874b0bde02271 GIT binary patch literal 6066 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's from expanding the number of files TypeScript should add to a project. */ + + /* JavaScript Support */ + // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ + // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ + // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ + + /* Emit */ + // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ + // "declarationMap": true, /* Create sourcemaps for d.ts files. */ + // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ + // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ + // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ + // "noEmit": true, /* Disable emitting files from a compilation. */ + // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ + "outDir": "./build", /* Specify an output folder for all emitted files. */ + // "removeComments": true, /* Disable emitting comments. */ + // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ + // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ + // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ + // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ + // "newLine": "crlf", /* Set the newline character for emitting files. */ + // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ + // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ + // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ + // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ + // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ + + /* Interop Constraints */ + // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ + // "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */ + // "isolatedDeclarations": true, /* Require sufficient annotation on exports so other tools can trivially generate declaration files. */ + // "erasableSyntaxOnly": true, /* Do not allow runtime constructs that are not part of ECMAScript. */ + // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ + "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ + // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ + "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ + + /* Type Checking */ + "strict": true, /* Enable all strict type-checking options. */ + // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ + // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ + // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ + // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ + // "strictBuiltinIteratorReturn": true, /* Built-in iterators are instantiated with a 'TReturn' type of 'undefined' instead of 'any'. */ + // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ + // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ + // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ + // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ + // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ + // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ + // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ + // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ + // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ + // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ + // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ + // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ + // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ + + /* Completeness */ + // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ + "skipLibCheck": true /* Skip type checking all .d.ts files. */ + } +}