From d4b2415cd50b8626efb1e8d4379f9aceb7c33697 Mon Sep 17 00:00:00 2001 From: jaxon Date: Thu, 25 Dec 2025 13:54:41 +0800 Subject: [PATCH 01/19] feat: Telegram and JWT auth --- backend/.gitignore | 4 +- backend/README.md | 35 + backend/package-lock.json | 2100 +++++++-------------- backend/package.json | 13 +- backend/src/routes/private/queue/hello.ts | 14 + backend/src/routes/public/auth/index.ts | 93 + backend/src/server.ts | 70 +- backend/src/types.ts | 29 + backend/tsconfig.json | 51 +- 9 files changed, 953 insertions(+), 1456 deletions(-) create mode 100644 backend/README.md create mode 100644 backend/src/routes/private/queue/hello.ts create mode 100644 backend/src/routes/public/auth/index.ts create mode 100644 backend/src/types.ts diff --git a/backend/.gitignore b/backend/.gitignore index 7702141..afdec6a 100644 --- a/backend/.gitignore +++ b/backend/.gitignore @@ -1,3 +1,5 @@ **/node_modules .env* -.DS_Store \ No newline at end of file +.DS_Store +.vercel +.env*.local diff --git a/backend/README.md b/backend/README.md new file mode 100644 index 0000000..97f23a3 --- /dev/null +++ b/backend/README.md @@ -0,0 +1,35 @@ +# QueueBot Backend + +--- + +## 🚀 Getting Started + +Follow these steps to get your local development environment up and running. + +### 1. Environment Configuration +Create a `.env` file in the root directory and add the development credentials: + +`BOT_TOKEN=your_development_bot_token` +`JWT_SECRET=your_jwt_secret_here` + +### 2. Install Dependencies +Install the required packages using npm: + +```bash +npm install +``` + +### 3. Launch Dev Server +```bash +npm run dev +``` + +## 📁 API Routing + +JWT check is toggled based on directory + +| Directory | Access Level | Description | +| :--- | :--- |:----------------------------------------| +| `src/routes/public` | **Unprotected** | Open endpoints like `/auth`. | +| `src/routes/private` | **Authenticated** | Requires a valid JWT to access. | + diff --git a/backend/package-lock.json b/backend/package-lock.json index df4c0ac..321d7e0 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -9,16 +9,17 @@ "version": "1.0.0", "license": "ISC", "dependencies": { - "@fastify/static": "^8.3.0", - "@prisma/client": "^7.2.0", + "@fastify/autoload": "^6.3.1", + "@fastify/cors": "^11.2.0", "fastify": "^5.6.2", + "jose": "^6.1.3", "zod": "^4.2.1" }, "devDependencies": { "@types/node": "^25.0.3", "dotenv": "^17.2.3", "prisma": "^7.2.0", - "ts-node-dev": "^2.0.0", + "tsx": "^4.21.0", "typescript": "^5.9.3" } }, @@ -26,7 +27,7 @@ "version": "10.5.0", "resolved": "https://registry.npmjs.org/@chevrotain/cst-dts-gen/-/cst-dts-gen-10.5.0.tgz", "integrity": "sha512-lhmC/FyqQ2o7pGK4Om+hzuDrm9rhFYIJ/AXoQBeongmn870Xeb0L6oGEiuR8nohFNL5sMaQEJWCxr1oIVIVXrw==", - "devOptional": true, + "dev": true, "license": "Apache-2.0", "dependencies": { "@chevrotain/gast": "10.5.0", @@ -38,7 +39,7 @@ "version": "10.5.0", "resolved": "https://registry.npmjs.org/@chevrotain/gast/-/gast-10.5.0.tgz", "integrity": "sha512-pXdMJ9XeDAbgOWKuD1Fldz4ieCs6+nLNmyVhe2gZVqoO7v8HXuHYs5OV2EzUtbuai37TlOAQHrTDvxMnvMJz3A==", - "devOptional": true, + "dev": true, "license": "Apache-2.0", "dependencies": { "@chevrotain/types": "10.5.0", @@ -49,34 +50,21 @@ "version": "10.5.0", "resolved": "https://registry.npmjs.org/@chevrotain/types/-/types-10.5.0.tgz", "integrity": "sha512-f1MAia0x/pAVPWH/T73BJVyO2XU5tI4/iE7cnxb7tqdNTNhQI3Uq3XkqcoteTmD4t1aM0LbHCJOhgIDn07kl2A==", - "devOptional": true, + "dev": true, "license": "Apache-2.0" }, "node_modules/@chevrotain/utils": { "version": "10.5.0", "resolved": "https://registry.npmjs.org/@chevrotain/utils/-/utils-10.5.0.tgz", "integrity": "sha512-hBzuU5+JjB2cqNZyszkDHZgOSrUUT8V3dhgRl8Q9Gp6dAj/H5+KILGjbhDpc3Iy9qmqlm/akuOI2ut9VUtzJxQ==", - "devOptional": true, - "license": "Apache-2.0" - }, - "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" - } + "license": "Apache-2.0" }, "node_modules/@electric-sql/pglite": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/@electric-sql/pglite/-/pglite-0.3.2.tgz", "integrity": "sha512-zfWWa+V2ViDCY/cmUfRqeWY1yLto+EpxjXnZzenB1TyxsTiXaTWeZFIZw6mac52BsuQm0RjCnisjBtdBaXOI6w==", - "devOptional": true, + "dev": true, "license": "Apache-2.0", "peer": true }, @@ -84,7 +72,7 @@ "version": "0.0.6", "resolved": "https://registry.npmjs.org/@electric-sql/pglite-socket/-/pglite-socket-0.0.6.tgz", "integrity": "sha512-6RjmgzphIHIBA4NrMGJsjNWK4pu+bCWJlEWlwcxFTVY3WT86dFpKwbZaGWZV6C5Rd7sCk1Z0CI76QEfukLAUXw==", - "devOptional": true, + "dev": true, "license": "Apache-2.0", "bin": { "pglite-server": "dist/scripts/server.js" @@ -97,27 +85,453 @@ "version": "0.2.7", "resolved": "https://registry.npmjs.org/@electric-sql/pglite-tools/-/pglite-tools-0.2.7.tgz", "integrity": "sha512-9dAccClqxx4cZB+Ar9B+FZ5WgxDc/Xvl9DPrTWv+dYTf0YNubLzi4wHHRGRGhrJv15XwnyKcGOZAP1VXSneSUg==", - "devOptional": true, + "dev": true, "license": "Apache-2.0", "peerDependencies": { "@electric-sql/pglite": "0.3.2" } }, - "node_modules/@fastify/accept-negotiator": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@fastify/accept-negotiator/-/accept-negotiator-2.0.1.tgz", - "integrity": "sha512-/c/TW2bO/v9JeEgoD/g1G5GxGeCF1Hafdf79WPmUlgYiBXummY0oX3VVq4yFkKKVBKDNlaDUYoab7g38RpPqCQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } + "node_modules/@esbuild/aix-ppc64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.2.tgz", + "integrity": "sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==", + "cpu": [ + "ppc64" ], - "license": "MIT" + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.2.tgz", + "integrity": "sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.2.tgz", + "integrity": "sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.2.tgz", + "integrity": "sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.2.tgz", + "integrity": "sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.2.tgz", + "integrity": "sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.2.tgz", + "integrity": "sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.2.tgz", + "integrity": "sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.2.tgz", + "integrity": "sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.2.tgz", + "integrity": "sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.2.tgz", + "integrity": "sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.2.tgz", + "integrity": "sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.2.tgz", + "integrity": "sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.2.tgz", + "integrity": "sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.2.tgz", + "integrity": "sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.2.tgz", + "integrity": "sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.2.tgz", + "integrity": "sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.2.tgz", + "integrity": "sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.2.tgz", + "integrity": "sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.2.tgz", + "integrity": "sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.2.tgz", + "integrity": "sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.2.tgz", + "integrity": "sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.2.tgz", + "integrity": "sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.2.tgz", + "integrity": "sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.2.tgz", + "integrity": "sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.2.tgz", + "integrity": "sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } }, "node_modules/@fastify/ajv-compiler": { "version": "4.0.5", @@ -140,10 +554,10 @@ "fast-uri": "^3.0.0" } }, - "node_modules/@fastify/error": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@fastify/error/-/error-4.2.0.tgz", - "integrity": "sha512-RSo3sVDXfHskiBZKBPRgnQTtIqpi/7zhJOEmAxCiBcM7d0uwdGdxLlsCaLzGs8v8NnxIRlfG0N51p5yFaOentQ==", + "node_modules/@fastify/autoload": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/@fastify/autoload/-/autoload-6.3.1.tgz", + "integrity": "sha512-0fsG+lO3m5yEZVjXKpltCe+2eHhM6rfAPQhvlGUgLUFTw/N2wA9WqPTObMtrF3oUCUrxbSDv60HlUIoh+aFM1A==", "funding": [ { "type": "github", @@ -156,10 +570,10 @@ ], "license": "MIT" }, - "node_modules/@fastify/fast-json-stringify-compiler": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/@fastify/fast-json-stringify-compiler/-/fast-json-stringify-compiler-5.0.3.tgz", - "integrity": "sha512-uik7yYHkLr6fxd8hJSZ8c+xF4WafPK+XzneQDPU+D10r5X19GW8lJcom2YijX2+qtFF1ENJlHXKFM9ouXNJYgQ==", + "node_modules/@fastify/cors": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/@fastify/cors/-/cors-11.2.0.tgz", + "integrity": "sha512-LbLHBuSAdGdSFZYTLVA3+Ch2t+sA6nq3Ejc6XLAKiQ6ViS2qFnvicpj0htsx03FyYeLs04HfRNBsz/a8SvbcUw==", "funding": [ { "type": "github", @@ -172,13 +586,14 @@ ], "license": "MIT", "dependencies": { - "fast-json-stringify": "^6.0.0" + "fastify-plugin": "^5.0.0", + "toad-cache": "^3.7.0" } }, - "node_modules/@fastify/forwarded": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@fastify/forwarded/-/forwarded-3.0.1.tgz", - "integrity": "sha512-JqDochHFqXs3C3Ml3gOY58zM7OqO9ENqPo0UqAjAjH8L01fRZqwX9iLeX34//kiJubF7r2ZQHtBRU36vONbLlw==", + "node_modules/@fastify/error": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@fastify/error/-/error-4.2.0.tgz", + "integrity": "sha512-RSo3sVDXfHskiBZKBPRgnQTtIqpi/7zhJOEmAxCiBcM7d0uwdGdxLlsCaLzGs8v8NnxIRlfG0N51p5yFaOentQ==", "funding": [ { "type": "github", @@ -191,10 +606,10 @@ ], "license": "MIT" }, - "node_modules/@fastify/merge-json-schemas": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/@fastify/merge-json-schemas/-/merge-json-schemas-0.2.1.tgz", - "integrity": "sha512-OA3KGBCy6KtIvLf8DINC5880o5iBlDX4SxzLQS8HorJAbqluzLRn80UXU0bxZn7UOFhFgpRJDasfwn9nG4FG4A==", + "node_modules/@fastify/fast-json-stringify-compiler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@fastify/fast-json-stringify-compiler/-/fast-json-stringify-compiler-5.0.3.tgz", + "integrity": "sha512-uik7yYHkLr6fxd8hJSZ8c+xF4WafPK+XzneQDPU+D10r5X19GW8lJcom2YijX2+qtFF1ENJlHXKFM9ouXNJYgQ==", "funding": [ { "type": "github", @@ -207,13 +622,13 @@ ], "license": "MIT", "dependencies": { - "dequal": "^2.0.3" + "fast-json-stringify": "^6.0.0" } }, - "node_modules/@fastify/proxy-addr": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@fastify/proxy-addr/-/proxy-addr-5.1.0.tgz", - "integrity": "sha512-INS+6gh91cLUjB+PVHfu1UqcB76Sqtpyp7bnL+FYojhjygvOPA9ctiD/JDKsyD9Xgu4hUhCSJBPig/w7duNajw==", + "node_modules/@fastify/forwarded": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@fastify/forwarded/-/forwarded-3.0.1.tgz", + "integrity": "sha512-JqDochHFqXs3C3Ml3gOY58zM7OqO9ENqPo0UqAjAjH8L01fRZqwX9iLeX34//kiJubF7r2ZQHtBRU36vONbLlw==", "funding": [ { "type": "github", @@ -224,16 +639,12 @@ "url": "https://opencollective.com/fastify" } ], - "license": "MIT", - "dependencies": { - "@fastify/forwarded": "^3.0.0", - "ipaddr.js": "^2.1.0" - } + "license": "MIT" }, - "node_modules/@fastify/send": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@fastify/send/-/send-4.1.0.tgz", - "integrity": "sha512-TMYeQLCBSy2TOFmV95hQWkiTYgC/SEx7vMdV+wnZVX4tt8VBLKzmH8vV9OzJehV0+XBfg+WxPMt5wp+JBUKsVw==", + "node_modules/@fastify/merge-json-schemas": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@fastify/merge-json-schemas/-/merge-json-schemas-0.2.1.tgz", + "integrity": "sha512-OA3KGBCy6KtIvLf8DINC5880o5iBlDX4SxzLQS8HorJAbqluzLRn80UXU0bxZn7UOFhFgpRJDasfwn9nG4FG4A==", "funding": [ { "type": "github", @@ -246,130 +657,47 @@ ], "license": "MIT", "dependencies": { - "@lukeed/ms": "^2.0.2", - "escape-html": "~1.0.3", - "fast-decode-uri-component": "^1.0.1", - "http-errors": "^2.0.0", - "mime": "^3" + "dequal": "^2.0.3" } }, - "node_modules/@fastify/static": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/@fastify/static/-/static-8.3.0.tgz", - "integrity": "sha512-yKxviR5PH1OKNnisIzZKmgZSus0r2OZb8qCSbqmw34aolT4g3UlzYfeBRym+HJ1J471CR8e2ldNub4PubD1coA==", + "node_modules/@fastify/proxy-addr": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@fastify/proxy-addr/-/proxy-addr-5.1.0.tgz", + "integrity": "sha512-INS+6gh91cLUjB+PVHfu1UqcB76Sqtpyp7bnL+FYojhjygvOPA9ctiD/JDKsyD9Xgu4hUhCSJBPig/w7duNajw==", "funding": [ { "type": "github", "url": "https://github.com/sponsors/fastify" }, { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "MIT", - "dependencies": { - "@fastify/accept-negotiator": "^2.0.0", - "@fastify/send": "^4.0.0", - "content-disposition": "^0.5.4", - "fastify-plugin": "^5.0.0", - "fastq": "^1.17.1", - "glob": "^11.0.0" - } - }, - "node_modules/@hono/node-server": { - "version": "1.19.6", - "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.6.tgz", - "integrity": "sha512-Shz/KjlIeAhfiuE93NDKVdZ7HdBVLQAfdbaXEaoAVO3ic9ibRSLGIQGkcBbFyuLr+7/1D5ZCINM8B+6IvXeMtw==", - "devOptional": true, - "license": "MIT", - "engines": { - "node": ">=18.14.1" - }, - "peerDependencies": { - "hono": "^4" - } - }, - "node_modules/@isaacs/balanced-match": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", - "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==", - "license": "MIT", - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/@isaacs/brace-expansion": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz", - "integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==", - "license": "MIT", - "dependencies": { - "@isaacs/balanced-match": "^4.0.1" - }, - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "license": "ISC", - "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" - }, - "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, + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], "license": "MIT", "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" + "@fastify/forwarded": "^3.0.0", + "ipaddr.js": "^2.1.0" } }, - "node_modules/@lukeed/ms": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@lukeed/ms/-/ms-2.0.2.tgz", - "integrity": "sha512-9I2Zn6+NJLfaGoz9jN3lpwDgAYvfGeNYdbAIjJOqzs4Tpc+VU3Jqq4IofSUBKajiDS8k9fZIg18/z13mpk1bsA==", + "node_modules/@hono/node-server": { + "version": "1.19.6", + "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.6.tgz", + "integrity": "sha512-Shz/KjlIeAhfiuE93NDKVdZ7HdBVLQAfdbaXEaoAVO3ic9ibRSLGIQGkcBbFyuLr+7/1D5ZCINM8B+6IvXeMtw==", + "dev": true, "license": "MIT", "engines": { - "node": ">=8" + "node": ">=18.14.1" + }, + "peerDependencies": { + "hono": "^4" } }, "node_modules/@mrleebo/prisma-ast": { "version": "0.12.1", "resolved": "https://registry.npmjs.org/@mrleebo/prisma-ast/-/prisma-ast-0.12.1.tgz", "integrity": "sha512-JwqeCQ1U3fvccttHZq7Tk0m/TMC6WcFAQZdukypW3AzlJYKYTGNVd1ANU2GuhKnv4UQuOFj3oAl0LLG/gxFN1w==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "chevrotain": "^10.5.0", @@ -385,41 +713,11 @@ "integrity": "sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==", "license": "MIT" }, - "node_modules/@prisma/client": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@prisma/client/-/client-7.2.0.tgz", - "integrity": "sha512-JdLF8lWZ+LjKGKpBqyAlenxd/kXjd1Abf/xK+6vUA7R7L2Suo6AFTHFRpPSdAKCan9wzdFApsUpSa/F6+t1AtA==", - "license": "Apache-2.0", - "dependencies": { - "@prisma/client-runtime-utils": "7.2.0" - }, - "engines": { - "node": "^20.19 || ^22.12 || >=24.0" - }, - "peerDependencies": { - "prisma": "*", - "typescript": ">=5.4.0" - }, - "peerDependenciesMeta": { - "prisma": { - "optional": true - }, - "typescript": { - "optional": true - } - } - }, - "node_modules/@prisma/client-runtime-utils": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@prisma/client-runtime-utils/-/client-runtime-utils-7.2.0.tgz", - "integrity": "sha512-dn7oB53v0tqkB0wBdMuTNFNPdEbfICEUe82Tn9FoKAhJCUkDH+fmyEp0ClciGh+9Hp2Tuu2K52kth2MTLstvmA==", - "license": "Apache-2.0" - }, "node_modules/@prisma/config": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/@prisma/config/-/config-7.2.0.tgz", "integrity": "sha512-qmvSnfQ6l/srBW1S7RZGfjTQhc44Yl3ldvU6y3pgmuLM+83SBDs6UQVgMtQuMRe9J3gGqB0RF8wER6RlXEr6jQ==", - "devOptional": true, + "dev": true, "license": "Apache-2.0", "dependencies": { "c12": "3.1.0", @@ -432,14 +730,14 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-7.2.0.tgz", "integrity": "sha512-YSGTiSlBAVJPzX4ONZmMotL+ozJwQjRmZweQNIq/ER0tQJKJynNkRB3kyvt37eOfsbMCXk3gnLF6J9OJ4QWftw==", - "devOptional": true, + "dev": true, "license": "Apache-2.0" }, "node_modules/@prisma/dev": { "version": "0.17.0", "resolved": "https://registry.npmjs.org/@prisma/dev/-/dev-0.17.0.tgz", "integrity": "sha512-6sGebe5jxX+FEsQTpjHLzvOGPn6ypFQprcs3jcuIWv1Xp/5v6P/rjfdvAwTkP2iF6pDx2tCd8vGLNWcsWzImTA==", - "devOptional": true, + "dev": true, "license": "ISC", "dependencies": { "@electric-sql/pglite": "0.3.2", @@ -465,7 +763,7 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-7.2.0.tgz", "integrity": "sha512-HUeOI/SvCDsHrR9QZn24cxxZcujOjcS3w1oW/XVhnSATAli5SRMOfp/WkG3TtT5rCxDA4xOnlJkW7xkho4nURA==", - "devOptional": true, + "dev": true, "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { @@ -479,14 +777,14 @@ "version": "7.2.0-4.0c8ef2ce45c83248ab3df073180d5eda9e8be7a3", "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-7.2.0-4.0c8ef2ce45c83248ab3df073180d5eda9e8be7a3.tgz", "integrity": "sha512-KezsjCZDsbjNR7SzIiVlUsn9PnLePI7r5uxABlwL+xoerurZTfgQVbIjvjF2sVr3Uc0ZcsnREw3F84HvbggGdA==", - "devOptional": true, + "dev": true, "license": "Apache-2.0" }, "node_modules/@prisma/engines/node_modules/@prisma/get-platform": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-7.2.0.tgz", "integrity": "sha512-k1V0l0Td1732EHpAfi2eySTezyllok9dXb6UQanajkJQzPUGi3vO2z7jdkz67SypFTdmbnyGYxvEvYZdZsMAVA==", - "devOptional": true, + "dev": true, "license": "Apache-2.0", "dependencies": { "@prisma/debug": "7.2.0" @@ -496,7 +794,7 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-7.2.0.tgz", "integrity": "sha512-Z5XZztJ8Ap+wovpjPD2lQKnB8nWFGNouCrglaNFjxIWAGWz0oeHXwUJRiclIoSSXN/ptcs9/behptSk8d0Yy6w==", - "devOptional": true, + "dev": true, "license": "Apache-2.0", "dependencies": { "@prisma/debug": "7.2.0", @@ -508,7 +806,7 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-7.2.0.tgz", "integrity": "sha512-k1V0l0Td1732EHpAfi2eySTezyllok9dXb6UQanajkJQzPUGi3vO2z7jdkz67SypFTdmbnyGYxvEvYZdZsMAVA==", - "devOptional": true, + "dev": true, "license": "Apache-2.0", "dependencies": { "@prisma/debug": "7.2.0" @@ -518,7 +816,7 @@ "version": "6.8.2", "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-6.8.2.tgz", "integrity": "sha512-vXSxyUgX3vm1Q70QwzwkjeYfRryIvKno1SXbIqwSptKwqKzskINnDUcx85oX+ys6ooN2ATGSD0xN2UTfg6Zcow==", - "devOptional": true, + "dev": true, "license": "Apache-2.0", "dependencies": { "@prisma/debug": "6.8.2" @@ -528,21 +826,21 @@ "version": "6.8.2", "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-6.8.2.tgz", "integrity": "sha512-4muBSSUwJJ9BYth5N8tqts8JtiLT8QI/RSAzEogwEfpbYGFo9mYsInsVo8dqXdPO2+Rm5OG5q0qWDDE3nyUbVg==", - "devOptional": true, + "dev": true, "license": "Apache-2.0" }, "node_modules/@prisma/query-plan-executor": { "version": "6.18.0", "resolved": "https://registry.npmjs.org/@prisma/query-plan-executor/-/query-plan-executor-6.18.0.tgz", "integrity": "sha512-jZ8cfzFgL0jReE1R10gT8JLHtQxjWYLiQ//wHmVYZ2rVkFHoh0DT8IXsxcKcFlfKN7ak7k6j0XMNn2xVNyr5cA==", - "devOptional": true, + "dev": true, "license": "Apache-2.0" }, "node_modules/@prisma/studio-core": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/@prisma/studio-core/-/studio-core-0.9.0.tgz", "integrity": "sha512-xA2zoR/ADu/NCSQuriBKTh6Ps4XjU0bErkEcgMfnSGh346K1VI7iWKnoq1l2DoxUqiddPHIEWwtxJ6xCHG6W7g==", - "devOptional": true, + "dev": true, "license": "Apache-2.0", "peerDependencies": { "@types/react": "^18.0.0 || ^19.0.0", @@ -554,34 +852,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", - "devOptional": true, - "license": "MIT" - }, - "node_modules/@tsconfig/node10": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.12.tgz", - "integrity": "sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ==", - "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" }, @@ -600,59 +870,19 @@ "version": "19.2.7", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.7.tgz", "integrity": "sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==", - "devOptional": true, + "dev": true, "license": "MIT", "peer": true, "dependencies": { "csstype": "^3.2.2" } }, - "node_modules/@types/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-xevGOReSYGM7g/kUBZzPqCrR/KYAo+F0yiPc85WFTJa0MSLtyFTVTU6cJu/aV4mid7IffDIWqo69THF2o4JiEQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/strip-json-comments": { - "version": "0.0.30", - "resolved": "https://registry.npmjs.org/@types/strip-json-comments/-/strip-json-comments-0.0.30.tgz", - "integrity": "sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ==", - "dev": true, - "license": "MIT" - }, "node_modules/abstract-logging": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/abstract-logging/-/abstract-logging-2.0.1.tgz", "integrity": "sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA==", "license": "MIT" }, - "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/ajv": { "version": "8.17.1", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", @@ -686,51 +916,6 @@ } } }, - "node_modules/ansi-regex": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", - "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/ansi-styles": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", - "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "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/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/atomic-sleep": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz", @@ -754,68 +939,17 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/aws-ssl-profiles/-/aws-ssl-profiles-1.1.2.tgz", "integrity": "sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g==", - "devOptional": true, - "license": "MIT", - "engines": { - "node": ">= 6.0.0" - } - }, - "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/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/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": ">= 6.0.0" } }, - "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==", - "dev": true, - "license": "MIT" - }, "node_modules/c12": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/c12/-/c12-3.1.0.tgz", "integrity": "sha512-uWoS8OU1MEIsOv8p/5a82c3H31LsWVR5qiyXVfBNOzfffjUWtPnhAb4BYI2uG2HfGmZmFjCtui5XNWaps+iFuw==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "chokidar": "^4.0.3", @@ -844,7 +978,7 @@ "version": "16.6.1", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", - "devOptional": true, + "dev": true, "license": "BSD-2-Clause", "engines": { "node": ">=12" @@ -857,7 +991,7 @@ "version": "10.5.0", "resolved": "https://registry.npmjs.org/chevrotain/-/chevrotain-10.5.0.tgz", "integrity": "sha512-Pkv5rBY3+CsHOYfV5g/Vs5JY9WTHHDEKOlohI2XeygaZhUeqhAlldZ8Hz9cRmxu709bvS08YzxHdTPHhffc13A==", - "devOptional": true, + "dev": true, "license": "Apache-2.0", "dependencies": { "@chevrotain/cst-dts-gen": "10.5.0", @@ -872,7 +1006,7 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "readdirp": "^4.0.1" @@ -888,66 +1022,29 @@ "version": "0.1.6", "resolved": "https://registry.npmjs.org/citty/-/citty-0.1.6.tgz", "integrity": "sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "consola": "^3.2.3" } }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" - }, - "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/confbox": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.2.tgz", "integrity": "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/consola": { "version": "3.4.2", "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": "^14.18.0 || >=16.10.0" } }, - "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==", - "license": "MIT", - "dependencies": { - "safe-buffer": "5.2.1" - }, - "engines": { - "node": ">= 0.6" - } - }, "node_modules/cookie": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz", @@ -961,17 +1058,11 @@ "url": "https://opencollective.com/express" } }, - "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/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, "license": "MIT", "dependencies": { "path-key": "^3.1.0", @@ -986,14 +1077,14 @@ "version": "3.2.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", - "devOptional": true, + "dev": true, "license": "MIT" }, "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==", - "devOptional": true, + "dev": true, "license": "BSD-3-Clause", "engines": { "node": ">=16.0.0" @@ -1003,28 +1094,19 @@ "version": "6.1.4", "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.4.tgz", "integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/denque": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==", - "devOptional": true, + "dev": true, "license": "Apache-2.0", "engines": { "node": ">=0.10" } }, - "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/dequal": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", @@ -1038,18 +1120,8 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/destr/-/destr-2.0.5.tgz", "integrity": "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==", - "devOptional": true, - "license": "MIT" - }, - "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" - } + "license": "MIT" }, "node_modules/dotenv": { "version": "17.2.3", @@ -1064,67 +1136,81 @@ "url": "https://dotenvx.com" } }, - "node_modules/dynamic-dedupe": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/dynamic-dedupe/-/dynamic-dedupe-0.3.0.tgz", - "integrity": "sha512-ssuANeD+z97meYOqd50e04Ze5qp4bPqo8cCkI4TRjZkzAUgIDTrXV1R8QCdINpiI+hw14+rYazvTRdQrz0/rFQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "xtend": "^4.0.0" - } - }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "license": "MIT" - }, "node_modules/effect": { "version": "3.18.4", "resolved": "https://registry.npmjs.org/effect/-/effect-3.18.4.tgz", "integrity": "sha512-b1LXQJLe9D11wfnOKAk3PKxuqYshQ0Heez+y5pnkd3jLj1yx9QhM72zZ9uUrOQyNvrs2GZZd/3maL0ZV18YuDA==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@standard-schema/spec": "^1.0.0", "fast-check": "^3.23.1" } }, - "node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "license": "MIT" - }, "node_modules/empathic": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/empathic/-/empathic-2.0.0.tgz", "integrity": "sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">=14" } }, - "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/esbuild": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.2.tgz", + "integrity": "sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.2", + "@esbuild/android-arm": "0.27.2", + "@esbuild/android-arm64": "0.27.2", + "@esbuild/android-x64": "0.27.2", + "@esbuild/darwin-arm64": "0.27.2", + "@esbuild/darwin-x64": "0.27.2", + "@esbuild/freebsd-arm64": "0.27.2", + "@esbuild/freebsd-x64": "0.27.2", + "@esbuild/linux-arm": "0.27.2", + "@esbuild/linux-arm64": "0.27.2", + "@esbuild/linux-ia32": "0.27.2", + "@esbuild/linux-loong64": "0.27.2", + "@esbuild/linux-mips64el": "0.27.2", + "@esbuild/linux-ppc64": "0.27.2", + "@esbuild/linux-riscv64": "0.27.2", + "@esbuild/linux-s390x": "0.27.2", + "@esbuild/linux-x64": "0.27.2", + "@esbuild/netbsd-arm64": "0.27.2", + "@esbuild/netbsd-x64": "0.27.2", + "@esbuild/openbsd-arm64": "0.27.2", + "@esbuild/openbsd-x64": "0.27.2", + "@esbuild/openharmony-arm64": "0.27.2", + "@esbuild/sunos-x64": "0.27.2", + "@esbuild/win32-arm64": "0.27.2", + "@esbuild/win32-ia32": "0.27.2", + "@esbuild/win32-x64": "0.27.2" + } }, "node_modules/exsolve": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.8.tgz", "integrity": "sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA==", - "devOptional": true, + "dev": true, "license": "MIT" }, "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==", - "devOptional": true, + "dev": true, "funding": [ { "type": "individual", @@ -1262,19 +1348,6 @@ "reusify": "^1.0.4" } }, - "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/find-my-way": { "version": "9.3.0", "resolved": "https://registry.npmjs.org/find-my-way/-/find-my-way-9.3.0.tgz", @@ -1293,6 +1366,7 @@ "version": "3.3.1", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, "license": "ISC", "dependencies": { "cross-spawn": "^7.0.6", @@ -1305,13 +1379,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "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==", - "dev": true, - "license": "ISC" - }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -1327,21 +1394,11 @@ "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==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/generate-function": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz", "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "is-property": "^1.0.2" @@ -1351,133 +1408,77 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/get-port-please/-/get-port-please-3.1.2.tgz", "integrity": "sha512-Gxc29eLs1fbn6LQ4jSU4vXjlwyZhF5HsGuMAa7gqBP4Rw4yxxltyDUuF5MBclFzDTXO+ACchGQoeela4DSfzdQ==", - "devOptional": true, + "dev": true, "license": "MIT" }, - "node_modules/giget": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/giget/-/giget-2.0.0.tgz", - "integrity": "sha512-L5bGsVkxJbJgdnwyuheIunkGatUF/zssUoxxjACCseZYAVbaqdh9Tsmmlkl8vYan09H7sbvKt4pS8GqKLBrEzA==", - "devOptional": true, + "node_modules/get-tsconfig": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.0.tgz", + "integrity": "sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==", + "dev": true, "license": "MIT", "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": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-11.1.0.tgz", - "integrity": "sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==", - "license": "BlueOak-1.0.0", - "dependencies": { - "foreground-child": "^3.3.1", - "jackspeak": "^4.1.1", - "minimatch": "^10.1.1", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^2.0.0" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "engines": { - "node": "20 || >=22" + "resolve-pkg-maps": "^1.0.0" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" } }, - "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==", + "node_modules/giget": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/giget/-/giget-2.0.0.tgz", + "integrity": "sha512-L5bGsVkxJbJgdnwyuheIunkGatUF/zssUoxxjACCseZYAVbaqdh9Tsmmlkl8vYan09H7sbvKt4pS8GqKLBrEzA==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "is-glob": "^4.0.1" + "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" }, - "engines": { - "node": ">= 6" + "bin": { + "giget": "dist/cli.mjs" } }, "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "devOptional": true, + "dev": true, "license": "ISC" }, "node_modules/grammex": { "version": "3.1.12", "resolved": "https://registry.npmjs.org/grammex/-/grammex-3.1.12.tgz", "integrity": "sha512-6ufJOsSA7LcQehIJNCO7HIBykfM7DXQual0Ny780/DEcJIpBlHRvcqEBWGPYd7hrXL2GJ3oJI1MIhaXjWmLQOQ==", - "devOptional": true, - "license": "MIT" - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", "dev": true, - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } + "license": "MIT" }, "node_modules/hono": { "version": "4.10.6", "resolved": "https://registry.npmjs.org/hono/-/hono-4.10.6.tgz", "integrity": "sha512-BIdolzGpDO9MQ4nu3AUuDwHZZ+KViNm+EZ75Ae55eMXMqLVhDFqEMXxtUe9Qh8hjL+pIna/frs2j6Y2yD5Ua/g==", - "devOptional": true, + "dev": true, "license": "MIT", "peer": true, "engines": { "node": ">=16.9.0" } }, - "node_modules/http-errors": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", - "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", - "license": "MIT", - "dependencies": { - "depd": "~2.0.0", - "inherits": "~2.0.4", - "setprototypeof": "~1.2.0", - "statuses": "~2.0.2", - "toidentifier": "~1.0.1" - }, - "engines": { - "node": ">= 0.8" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, "node_modules/http-status-codes": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/http-status-codes/-/http-status-codes-2.3.0.tgz", "integrity": "sha512-RJ8XvFvpPM/Dmc5SV+dC4y5PCeOhT3x1Hq0NU3rjGeg5a/CqlhZ7uudknPwZFz4aeAXDcbAyaeP7GAo9lvngtA==", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/iconv-lite": { "version": "0.7.1", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.1.tgz", "integrity": "sha512-2Tth85cXwGFHfvRgZWszZSvdo+0Xsqmw8k8ZwxScfcBneNUraK+dxRxRm24nszx80Y0TVio8kKLt5sLE7ZCLlw==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" @@ -1490,24 +1491,6 @@ "url": "https://opencollective.com/express" } }, - "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.", - "dev": true, - "license": "ISC", - "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==", - "license": "ISC" - }, "node_modules/ipaddr.js": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.3.0.tgz", @@ -1517,115 +1500,39 @@ "node": ">= 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-core-module": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", - "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", - "dev": true, - "license": "MIT", - "dependencies": { - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "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-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==", - "license": "MIT", - "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, - "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-property": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", "integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, "license": "ISC" }, - "node_modules/jackspeak": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.1.tgz", - "integrity": "sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==", - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/jiti": { "version": "2.6.1", "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", - "devOptional": true, + "dev": true, "license": "MIT", "bin": { "jiti": "lib/jiti-cli.mjs" } }, + "node_modules/jose": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/jose/-/jose-6.1.3.tgz", + "integrity": "sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, "node_modules/json-schema-ref-resolver": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/json-schema-ref-resolver/-/json-schema-ref-resolver-3.0.0.tgz", @@ -1692,7 +1599,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">=10" @@ -1702,30 +1609,21 @@ "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/long": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", - "devOptional": true, + "dev": true, "license": "Apache-2.0" }, - "node_modules/lru-cache": { - "version": "11.2.4", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.4.tgz", - "integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==", - "license": "BlueOak-1.0.0", - "engines": { - "node": "20 || >=22" - } - }, "node_modules/lru.min": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/lru.min/-/lru.min-1.1.3.tgz", "integrity": "sha512-Lkk/vx6ak3rYkRR0Nhu4lFUT2VDnQSxBe8Hbl7f36358p6ow8Bnvr8lrLt98H8J1aGxfhbX4Fs5tYg2+FTwr5Q==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "bun": ">=1.0.0", @@ -1737,77 +1635,11 @@ "url": "https://github.com/sponsors/wellwelwel" } }, - "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/mime": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", - "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", - "license": "MIT", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/minimatch": { - "version": "10.1.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.1.tgz", - "integrity": "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==", - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/brace-expansion": "^5.0.0" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "license": "ISC", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true, - "license": "MIT", - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/mysql2": { "version": "3.15.3", "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.15.3.tgz", "integrity": "sha512-FBrGau0IXmuqg4haEZRBfHNWB5mUARw6hNwPDXXGg0XzVJ50mr/9hb267lvpVMnhZ1FON3qNd4Xfcez1rbFwSg==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "aws-ssl-profiles": "^1.1.1", @@ -1828,7 +1660,7 @@ "version": "1.1.6", "resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.6.tgz", "integrity": "sha512-Tz09sEL2EEuv5fFowm419c1+a/jSMiBjI9gHxVLrVdbUkkNUUfjsVYs9pVZu5oCon/kmRh9TfLEObFtkVxmY0w==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "lru.min": "^1.1.0" @@ -1841,24 +1673,14 @@ "version": "1.6.7", "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.7.tgz", "integrity": "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==", - "devOptional": true, - "license": "MIT" - }, - "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" - } + "license": "MIT" }, "node_modules/nypm": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/nypm/-/nypm-0.6.2.tgz", "integrity": "sha512-7eM+hpOtrKrBDCh7Ypu2lJ9Z7PNZBdi/8AT3AX8xoCj43BBVHD0hPSTEvMtkMpfs8FCqBGhxB+uToIQimA111g==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "citty": "^0.1.6", @@ -1878,7 +1700,7 @@ "version": "2.0.11", "resolved": "https://registry.npmjs.org/ohash/-/ohash-2.0.11.tgz", "integrity": "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/on-exit-leak-free": { @@ -1890,90 +1712,29 @@ "node": ">=14.0.0" } }, - "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==", - "dev": true, - "license": "ISC", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/package-json-from-dist": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", - "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", - "license": "BlueOak-1.0.0" - }, - "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==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" } }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true, - "license": "MIT" - }, - "node_modules/path-scurry": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.1.tgz", - "integrity": "sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA==", - "license": "BlueOak-1.0.0", - "dependencies": { - "lru-cache": "^11.0.0", - "minipass": "^7.1.2" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/pathe": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", - "devOptional": true, + "dev": true, "license": "MIT" }, "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==", - "devOptional": true, - "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, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } + "license": "MIT" }, "node_modules/pino": { "version": "10.1.0", @@ -2016,7 +1777,7 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.3.0.tgz", "integrity": "sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "confbox": "^0.2.2", @@ -2028,7 +1789,7 @@ "version": "3.4.7", "resolved": "https://registry.npmjs.org/postgres/-/postgres-3.4.7.tgz", "integrity": "sha512-Jtc2612XINuBjIl/QTWsV5UvE8UHuNblcO3vVADSrKsrc6RqGX6lOW1cEo3CM2v0XG4Nat8nI+YM7/f26VxXLw==", - "devOptional": true, + "dev": true, "license": "Unlicense", "engines": { "node": ">=12" @@ -2042,10 +1803,9 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/prisma/-/prisma-7.2.0.tgz", "integrity": "sha512-jSdHWgWOgFF24+nRyyNRVBIgGDQEsMEF8KPHvhBBg3jWyR9fUAK0Nq9ThUmiGlNgq2FA7vSk/ZoCvefod+a8qg==", - "devOptional": true, + "dev": true, "hasInstallScript": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "@prisma/config": "7.2.0", "@prisma/dev": "0.17.0", @@ -2093,7 +1853,7 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz", "integrity": "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "graceful-fs": "^4.2.4", @@ -2105,14 +1865,14 @@ "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "devOptional": true, + "dev": true, "license": "ISC" }, "node_modules/pure-rand": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", - "devOptional": true, + "dev": true, "funding": [ { "type": "individual", @@ -2135,7 +1895,7 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/rc9/-/rc9-2.1.2.tgz", "integrity": "sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "defu": "^6.1.4", @@ -2146,7 +1906,7 @@ "version": "19.2.3", "resolved": "https://registry.npmjs.org/react/-/react-19.2.3.tgz", "integrity": "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==", - "devOptional": true, + "dev": true, "license": "MIT", "peer": true, "engines": { @@ -2157,7 +1917,7 @@ "version": "19.2.3", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.3.tgz", "integrity": "sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==", - "devOptional": true, + "dev": true, "license": "MIT", "peer": true, "dependencies": { @@ -2171,7 +1931,7 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">= 14.18.0" @@ -2194,14 +1954,14 @@ "version": "0.5.0", "resolved": "https://registry.npmjs.org/regexp-to-ast/-/regexp-to-ast-0.5.0.tgz", "integrity": "sha512-tlbJqcMHnPKI9zSrystikWKwHkBqu2a/Sgw01h3zFjvYrMxEDYHzzoMZnUrbIfpTFEsoRnnviOXNCzFiSc54Qw==", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/remeda": { "version": "2.21.3", "resolved": "https://registry.npmjs.org/remeda/-/remeda-2.21.3.tgz", "integrity": "sha512-XXrZdLA10oEOQhLLzEJEiFFSKi21REGAkHdImIb4rt/XXy8ORGXh5HCcpUOsElfPNDb+X6TA/+wkh+p2KffYmg==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "type-fest": "^4.39.1" @@ -2216,25 +1976,14 @@ "node": ">=0.10.0" } }, - "node_modules/resolve": { - "version": "1.22.11", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", - "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", "dev": true, "license": "MIT", - "dependencies": { - "is-core-module": "^2.16.1", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "engines": { - "node": ">= 0.4" - }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" } }, "node_modules/ret": { @@ -2250,95 +1999,26 @@ "version": "0.12.0", "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">= 4" } }, - "node_modules/reusify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", - "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", - "license": "MIT", - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/rfdc": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", - "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", - "license": "MIT" - }, - "node_modules/rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", - "dev": true, - "license": "ISC", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - } - }, - "node_modules/rimraf/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", - "dev": true, - "license": "ISC", - "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/rimraf/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" - }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "license": "MIT", "engines": { - "node": "*" + "iojs": ">=1.0.0", + "node": ">=0.10.0" } }, - "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/rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", "license": "MIT" }, "node_modules/safe-regex2": { @@ -2373,14 +2053,14 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/scheduler": { "version": "0.27.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/secure-json-parse": { @@ -2415,7 +2095,7 @@ "version": "0.0.5", "resolved": "https://registry.npmjs.org/seq-queue/-/seq-queue-0.0.5.tgz", "integrity": "sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q==", - "devOptional": true + "dev": true }, "node_modules/set-cookie-parser": { "version": "2.7.2", @@ -2423,16 +2103,11 @@ "integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==", "license": "MIT" }, - "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/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, "license": "MIT", "dependencies": { "shebang-regex": "^3.0.0" @@ -2445,6 +2120,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -2454,6 +2130,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, "license": "ISC", "engines": { "node": ">=14" @@ -2471,27 +2148,6 @@ "atomic-sleep": "^1.0.0" } }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "dev": true, - "license": "MIT", - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, "node_modules/split2": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", @@ -2505,156 +2161,18 @@ "version": "2.3.3", "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.3.tgz", "integrity": "sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">= 0.6" } }, - "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/std-env": { "version": "3.9.0", "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.9.0.tgz", "integrity": "sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==", - "devOptional": true, - "license": "MIT" - }, - "node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "license": "MIT", - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/string-width-cjs": { - "name": "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==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs/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==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs/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==", - "license": "MIT" - }, - "node_modules/string-width-cjs/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==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", - "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/strip-ansi-cjs": { - "name": "strip-ansi", - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi-cjs/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==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } + "license": "MIT" }, "node_modules/thread-stream": { "version": "3.1.0", @@ -2669,23 +2187,10 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz", "integrity": "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==", - "devOptional": true, - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "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": ">=18" } }, "node_modules/toad-cache": { @@ -2697,160 +2202,31 @@ "node": ">=12" } }, - "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/tree-kill": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", - "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", - "dev": true, - "license": "MIT", - "bin": { - "tree-kill": "cli.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/ts-node-dev": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ts-node-dev/-/ts-node-dev-2.0.0.tgz", - "integrity": "sha512-ywMrhCfH6M75yftYvrvNarLEY+SUXtUvU8/0Z6llrHQVBx12GiFk5sStF8UdfE/yfzk9IAq7O5EEbTQsxlBI8w==", + "node_modules/tsx": { + "version": "4.21.0", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz", + "integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==", "dev": true, "license": "MIT", "dependencies": { - "chokidar": "^3.5.1", - "dynamic-dedupe": "^0.3.0", - "minimist": "^1.2.6", - "mkdirp": "^1.0.4", - "resolve": "^1.0.0", - "rimraf": "^2.6.1", - "source-map-support": "^0.5.12", - "tree-kill": "^1.2.2", - "ts-node": "^10.4.0", - "tsconfig": "^7.0.0" + "esbuild": "~0.27.0", + "get-tsconfig": "^4.7.5" }, "bin": { - "ts-node-dev": "lib/bin.js", - "tsnd": "lib/bin.js" - }, - "engines": { - "node": ">=0.8.0" - }, - "peerDependencies": { - "node-notifier": "*", - "typescript": "*" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/ts-node-dev/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" + "tsx": "dist/cli.mjs" }, "engines": { - "node": ">= 8.10.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" + "node": ">=18.0.0" }, "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/ts-node-dev/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/tsconfig": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/tsconfig/-/tsconfig-7.0.0.tgz", - "integrity": "sha512-vZXmzPrL+EmC4T/4rVlT2jNVMWCi/O4DIiSj3UHg1OE5kCKbk4mfrXc6dZksLgRM/TZlKnousKH9bbTazUWRRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/strip-bom": "^3.0.0", - "@types/strip-json-comments": "0.0.30", - "strip-bom": "^3.0.0", - "strip-json-comments": "^2.0.0" + "fsevents": "~2.3.3" } }, "node_modules/type-fest": { "version": "4.41.0", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", - "devOptional": true, + "dev": true, "license": "(MIT OR CC0-1.0)", "engines": { "node": ">=16" @@ -2863,7 +2239,7 @@ "version": "5.9.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", - "devOptional": true, + "dev": true, "license": "Apache-2.0", "peer": true, "bin": { @@ -2881,18 +2257,11 @@ "dev": true, "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/valibot": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/valibot/-/valibot-1.2.0.tgz", "integrity": "sha512-mm1rxUsmOxzrwnX5arGS+U4T25RdvpPjPN4yR0u9pUBov9+zGVtO84tif1eY4r6zWxVxu3KzIyknJy3rxfRZZg==", - "devOptional": true, + "dev": true, "license": "MIT", "peerDependencies": { "typescript": ">=5" @@ -2907,6 +2276,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, "license": "ISC", "dependencies": { "isexe": "^2.0.0" @@ -2918,129 +2288,11 @@ "node": ">= 8" } }, - "node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs": { - "name": "wrap-ansi", - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs/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==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs/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==", - "license": "MIT" - }, - "node_modules/wrap-ansi-cjs/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==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi-cjs/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==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=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==", - "dev": true, - "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==", - "dev": true, - "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" - } - }, "node_modules/zeptomatch": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/zeptomatch/-/zeptomatch-2.0.2.tgz", "integrity": "sha512-H33jtSKf8Ijtb5BW6wua3G5DhnFjbFML36eFu+VdOoVY4HD9e7ggjqdM6639B+L87rjnR6Y+XeRzBXZdy52B/g==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "grammex": "^3.1.10" diff --git a/backend/package.json b/backend/package.json index deae28c..7c24c73 100644 --- a/backend/package.json +++ b/backend/package.json @@ -2,9 +2,9 @@ "name": "backend", "version": "1.0.0", "description": "", - "main": "index.js", + "main": "src/server.ts", "scripts": { - "dev": "ts-node-dev --respawn src/server.ts", + "dev": "tsx --env-file=.env src/server.ts", "build": "tsc", "start": "node dist/server.js", "test": "echo \"Error: no test specified\" && exit 1" @@ -12,18 +12,19 @@ "keywords": [], "author": "", "license": "ISC", - "type": "commonjs", + "type": "module", "dependencies": { - "@fastify/static": "^8.3.0", - "@prisma/client": "^7.2.0", + "@fastify/autoload": "^6.3.1", + "@fastify/cors": "^11.2.0", "fastify": "^5.6.2", + "jose": "^6.1.3", "zod": "^4.2.1" }, "devDependencies": { "@types/node": "^25.0.3", "dotenv": "^17.2.3", "prisma": "^7.2.0", - "ts-node-dev": "^2.0.0", + "tsx": "^4.21.0", "typescript": "^5.9.3" } } diff --git a/backend/src/routes/private/queue/hello.ts b/backend/src/routes/private/queue/hello.ts new file mode 100644 index 0000000..dc2585e --- /dev/null +++ b/backend/src/routes/private/queue/hello.ts @@ -0,0 +1,14 @@ +import type {FastifyPluginAsync} from 'fastify'; + + +const route: FastifyPluginAsync = async (fastify, _) => { + + fastify.get('/hello', async (request, reply) => { + + return reply.code(200).send(request.userId); + + }); + +}; + +export default route; \ No newline at end of file diff --git a/backend/src/routes/public/auth/index.ts b/backend/src/routes/public/auth/index.ts new file mode 100644 index 0000000..1263156 --- /dev/null +++ b/backend/src/routes/public/auth/index.ts @@ -0,0 +1,93 @@ +import {type FastifyPluginAsync} from 'fastify'; +import * as jose from "jose"; +import {createHmac} from "node:crypto"; +import type {TelegramWebAppData, TelegramWebUser} from "../../../types.js"; + + +const BOT_TOKEN = process.env.BOT_TOKEN!; +const JWT_SECRET = process.env.JWT_SECRET!; + +async function newJWT(userId: number) { + + const secret = new TextEncoder().encode( + JWT_SECRET + ); + + return await new jose.SignJWT({userId: userId}) + .setProtectedHeader({alg: 'HS256'}) + .setIssuedAt() + .setExpirationTime('2h') + .sign(secret); +} + +function parseAuthData(raw: string) : TelegramWebAppData { + const params = new URLSearchParams(raw); + return { + auth_date: params.get('auth_date') ?? '', + hash: params.get('hash') ?? '', + signature: params.get('signature') ?? '', + chat_instance: params.get('chat_instance')!, + chat_type: params.get('chat_type') as TelegramWebAppData['chat_type'], + user: JSON.parse(params.get('user') || '{}') as TelegramWebUser + }; +} + +function validate(raw: URLSearchParams, appData: TelegramWebAppData, token: string | undefined) { + + if (!appData.hash) throw new Error("Missing hash"); + + if (!token) throw new Error("Missing token"); + + const dataToCheck: string[] = []; + + for (const [key, value] of raw.entries()) { + // hash field should not be included + if (key !== 'hash') { + dataToCheck.push(`${key}=${value}`); + } + } + + dataToCheck.sort(); // Telegram requires alphabetical order + + const dataCheckString = dataToCheck.join('\n'); + + const secretKey = createHmac('sha256', 'WebAppData') + .update(token) + .digest(); + + const calculatedHash = createHmac('sha256', secretKey) + .update(dataCheckString) + .digest('hex'); + + if (calculatedHash !== appData.hash) { + throw new Error("Invalid signature"); + } + +} + +const route: FastifyPluginAsync = async (fastify, _) => { + + fastify.get('/', async (request, reply) => { + + const [authType, authData = ''] = (request.headers.authorization || '').split(' '); + + switch (authType) { + case 'tma': + try { + const appData = parseAuthData(authData); + validate(new URLSearchParams(authData), appData, BOT_TOKEN); + return reply.code(200).send({token: await newJWT(appData.user.id)}); + } catch (e) { + console.error(e); + return reply.code(500).send({"error": e}); + } + default: + return reply.code(401).send({"error": "Unauthorized"}); + } + + + }); + +}; + +export default route; \ No newline at end of file diff --git a/backend/src/server.ts b/backend/src/server.ts index 23fb54c..7ca4bdf 100644 --- a/backend/src/server.ts +++ b/backend/src/server.ts @@ -1,20 +1,64 @@ -import Fastify from "fastify"; -import fastifyStatic from "@fastify/static"; -import path from "path"; +import Fastify, {type FastifyReply, type FastifyRequest} from 'fastify'; +import cors from '@fastify/cors' +import * as jose from 'jose' +import autoLoad from '@fastify/autoload'; +import { join } from 'path'; -const server = Fastify({ logger: true }); +const JWT_SECRET = process.env.JWT_SECRET!; -/* Test if server working, delete when actually start dev -server.register(fastifyStatic, { - root: path.join(process.cwd(), "public"), +const fastify = Fastify({ + logger: true }); -server.get("/test", async () => { - return { running: true }; +await fastify.register(cors, { + origin: true, + methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'], + allowedHeaders: ['Content-Type', 'Authorization', 'User-Id'], + credentials: true, }); -server.listen({ port: 3000, host: "0.0.0.0" }).catch((err) => { - server.log.error(err); - process.exit(1); +const authHook = async (request: FastifyRequest, reply: FastifyReply) => { + + const secret = new TextEncoder().encode( + JWT_SECRET + ); + + try { + + if (request.headers.authorization === undefined) { + return reply.code(400).send({"error": "Missing JWT token"}); + } + const { payload } = await jose.jwtVerify(request.headers.authorization!, secret); + + request.userId = payload.userId as number; + + } catch (error) { + return reply.code(401).send({"error": "Unauthorized"}) + } + +}; + +fastify.register((fastify) => { + + fastify.addHook("preHandler", authHook); + + fastify.register(autoLoad, { + dir: join(process.cwd(), 'src/routes/private'), + }); + }); -*/ + + +fastify.register(autoLoad, { + dir: join(process.cwd(), 'src/routes/public'), +}); + +const start = async () => { + try { + await fastify.listen({ port: 3000 }) + } catch (err) { + fastify.log.error(err) + process.exit(1) + } +} +start(); diff --git a/backend/src/types.ts b/backend/src/types.ts new file mode 100644 index 0000000..584777f --- /dev/null +++ b/backend/src/types.ts @@ -0,0 +1,29 @@ +import 'fastify'; + +declare module 'fastify' { + interface FastifyRequest { + userId: number; + } +} + +export interface TelegramWebUser { + id: number; + first_name: string; + last_name?: string; + username?: string; + language_code?: string; + is_premium?: boolean; + allows_write_to_pm?: boolean; + photo_url?: string; +} + +export interface TelegramWebAppData { + auth_date: string; + hash: string; + signature: string; + user: TelegramWebUser; + chat_instance?: string; + chat_type?: 'sender' | 'private' | 'group' | 'supergroup' | 'channel' | undefined; + start_param?: string; + can_send_after?: number; +} diff --git a/backend/tsconfig.json b/backend/tsconfig.json index 4e78454..6983f40 100644 --- a/backend/tsconfig.json +++ b/backend/tsconfig.json @@ -1,19 +1,46 @@ { + // Visit https://aka.ms/tsconfig to read more about this file "compilerOptions": { - /* Base */ - "target": "es2022", + // File Layout + "rootDir": "./src", + "outDir": "./dist", + + // Environment Settings + // See also https://aka.ms/tsconfig/module + "module": "NodeNext", + "target": "esnext", "esModuleInterop": true, - "allowJs": true, - "resolveJsonModule": true, - "moduleDetection": "force", + "moduleResolution": "NodeNext", + "types": [], + // For nodejs: + // "lib": ["esnext"], + // "types": ["node"], + // and npm install -D @types/node - /* Strictness */ - "strict": true, - // "noUncheckedIndexedAccess": true + // Other Outputs + "sourceMap": true, + "declaration": true, + "declarationMap": true, + + // Stricter Typechecking Options + "noUncheckedIndexedAccess": true, + "exactOptionalPropertyTypes": true, - "module": "CommonJS", - "rootDir": "src", - "outDir": "dist", - "types": ["node"] + // Style Options + // "noImplicitReturns": true, + // "noImplicitOverride": true, + // "noUnusedLocals": true, + // "noUnusedParameters": true, + // "noFallthroughCasesInSwitch": true, + // "noPropertyAccessFromIndexSignature": true, + + // Recommended Options + "strict": true, + "jsx": "react-jsx", + "verbatimModuleSyntax": true, + "isolatedModules": true, + "noUncheckedSideEffectImports": true, + "moduleDetection": "force", + "skipLibCheck": true } } From 15d2c92c5c6ded9759b2bc76a90343d1571300c5 Mon Sep 17 00:00:00 2001 From: jaxon Date: Fri, 26 Dec 2025 23:28:26 +0800 Subject: [PATCH 02/19] set up prisma client and connected it to dev pg instance --- backend/.gitignore | 1 + backend/README.md | 8 +- backend/package-lock.json | 384 +++++++++++++----- backend/package.json | 2 + backend/prisma.config.ts | 5 +- .../20251226152623_init/migration.sql | 28 ++ backend/prisma/migrations/migration_lock.toml | 3 + backend/prisma/schema.prisma | 22 +- backend/src/prismaPlugin.ts | 31 ++ backend/src/routes/public/index.ts | 12 + backend/src/server.ts | 4 + 11 files changed, 397 insertions(+), 103 deletions(-) create mode 100644 backend/prisma/migrations/20251226152623_init/migration.sql create mode 100644 backend/prisma/migrations/migration_lock.toml create mode 100644 backend/src/prismaPlugin.ts create mode 100644 backend/src/routes/public/index.ts diff --git a/backend/.gitignore b/backend/.gitignore index afdec6a..2f4e6ad 100644 --- a/backend/.gitignore +++ b/backend/.gitignore @@ -3,3 +3,4 @@ .DS_Store .vercel .env*.local +src/generated \ No newline at end of file diff --git a/backend/README.md b/backend/README.md index 97f23a3..7b644b2 100644 --- a/backend/README.md +++ b/backend/README.md @@ -9,8 +9,9 @@ Follow these steps to get your local development environment up and running. ### 1. Environment Configuration Create a `.env` file in the root directory and add the development credentials: -`BOT_TOKEN=your_development_bot_token` +`BOT_TOKEN=your_development_bot_token_here` `JWT_SECRET=your_jwt_secret_here` +`DATABASE_URL=your_database_url_here` ### 2. Install Dependencies Install the required packages using npm: @@ -19,6 +20,11 @@ Install the required packages using npm: npm install ``` +### 3. Generate Prisma Client +```bash +npx prisma generate +``` + ### 3. Launch Dev Server ```bash npm run dev diff --git a/backend/package-lock.json b/backend/package-lock.json index 321d7e0..3ff3d40 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -11,6 +11,8 @@ "dependencies": { "@fastify/autoload": "^6.3.1", "@fastify/cors": "^11.2.0", + "@prisma/adapter-pg": "^7.2.0", + "@prisma/client": "^7.2.0", "fastify": "^5.6.2", "jose": "^6.1.3", "zod": "^4.2.1" @@ -27,7 +29,7 @@ "version": "10.5.0", "resolved": "https://registry.npmjs.org/@chevrotain/cst-dts-gen/-/cst-dts-gen-10.5.0.tgz", "integrity": "sha512-lhmC/FyqQ2o7pGK4Om+hzuDrm9rhFYIJ/AXoQBeongmn870Xeb0L6oGEiuR8nohFNL5sMaQEJWCxr1oIVIVXrw==", - "dev": true, + "devOptional": true, "license": "Apache-2.0", "dependencies": { "@chevrotain/gast": "10.5.0", @@ -39,7 +41,7 @@ "version": "10.5.0", "resolved": "https://registry.npmjs.org/@chevrotain/gast/-/gast-10.5.0.tgz", "integrity": "sha512-pXdMJ9XeDAbgOWKuD1Fldz4ieCs6+nLNmyVhe2gZVqoO7v8HXuHYs5OV2EzUtbuai37TlOAQHrTDvxMnvMJz3A==", - "dev": true, + "devOptional": true, "license": "Apache-2.0", "dependencies": { "@chevrotain/types": "10.5.0", @@ -50,21 +52,21 @@ "version": "10.5.0", "resolved": "https://registry.npmjs.org/@chevrotain/types/-/types-10.5.0.tgz", "integrity": "sha512-f1MAia0x/pAVPWH/T73BJVyO2XU5tI4/iE7cnxb7tqdNTNhQI3Uq3XkqcoteTmD4t1aM0LbHCJOhgIDn07kl2A==", - "dev": true, + "devOptional": true, "license": "Apache-2.0" }, "node_modules/@chevrotain/utils": { "version": "10.5.0", "resolved": "https://registry.npmjs.org/@chevrotain/utils/-/utils-10.5.0.tgz", "integrity": "sha512-hBzuU5+JjB2cqNZyszkDHZgOSrUUT8V3dhgRl8Q9Gp6dAj/H5+KILGjbhDpc3Iy9qmqlm/akuOI2ut9VUtzJxQ==", - "dev": true, + "devOptional": true, "license": "Apache-2.0" }, "node_modules/@electric-sql/pglite": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/@electric-sql/pglite/-/pglite-0.3.2.tgz", "integrity": "sha512-zfWWa+V2ViDCY/cmUfRqeWY1yLto+EpxjXnZzenB1TyxsTiXaTWeZFIZw6mac52BsuQm0RjCnisjBtdBaXOI6w==", - "dev": true, + "devOptional": true, "license": "Apache-2.0", "peer": true }, @@ -72,7 +74,7 @@ "version": "0.0.6", "resolved": "https://registry.npmjs.org/@electric-sql/pglite-socket/-/pglite-socket-0.0.6.tgz", "integrity": "sha512-6RjmgzphIHIBA4NrMGJsjNWK4pu+bCWJlEWlwcxFTVY3WT86dFpKwbZaGWZV6C5Rd7sCk1Z0CI76QEfukLAUXw==", - "dev": true, + "devOptional": true, "license": "Apache-2.0", "bin": { "pglite-server": "dist/scripts/server.js" @@ -85,7 +87,7 @@ "version": "0.2.7", "resolved": "https://registry.npmjs.org/@electric-sql/pglite-tools/-/pglite-tools-0.2.7.tgz", "integrity": "sha512-9dAccClqxx4cZB+Ar9B+FZ5WgxDc/Xvl9DPrTWv+dYTf0YNubLzi4wHHRGRGhrJv15XwnyKcGOZAP1VXSneSUg==", - "dev": true, + "devOptional": true, "license": "Apache-2.0", "peerDependencies": { "@electric-sql/pglite": "0.3.2" @@ -684,7 +686,7 @@ "version": "1.19.6", "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.6.tgz", "integrity": "sha512-Shz/KjlIeAhfiuE93NDKVdZ7HdBVLQAfdbaXEaoAVO3ic9ibRSLGIQGkcBbFyuLr+7/1D5ZCINM8B+6IvXeMtw==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">=18.14.1" @@ -697,7 +699,7 @@ "version": "0.12.1", "resolved": "https://registry.npmjs.org/@mrleebo/prisma-ast/-/prisma-ast-0.12.1.tgz", "integrity": "sha512-JwqeCQ1U3fvccttHZq7Tk0m/TMC6WcFAQZdukypW3AzlJYKYTGNVd1ANU2GuhKnv4UQuOFj3oAl0LLG/gxFN1w==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "chevrotain": "^10.5.0", @@ -713,11 +715,52 @@ "integrity": "sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==", "license": "MIT" }, + "node_modules/@prisma/adapter-pg": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@prisma/adapter-pg/-/adapter-pg-7.2.0.tgz", + "integrity": "sha512-euIdQ13cRB2wZ3jPsnDnFhINquo1PYFPCg6yVL8b2rp3EdinQHsX9EDdCtRr489D5uhphcRk463OdQAFlsCr0w==", + "license": "Apache-2.0", + "dependencies": { + "@prisma/driver-adapter-utils": "7.2.0", + "pg": "^8.16.3", + "postgres-array": "3.0.4" + } + }, + "node_modules/@prisma/client": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@prisma/client/-/client-7.2.0.tgz", + "integrity": "sha512-JdLF8lWZ+LjKGKpBqyAlenxd/kXjd1Abf/xK+6vUA7R7L2Suo6AFTHFRpPSdAKCan9wzdFApsUpSa/F6+t1AtA==", + "license": "Apache-2.0", + "dependencies": { + "@prisma/client-runtime-utils": "7.2.0" + }, + "engines": { + "node": "^20.19 || ^22.12 || >=24.0" + }, + "peerDependencies": { + "prisma": "*", + "typescript": ">=5.4.0" + }, + "peerDependenciesMeta": { + "prisma": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/@prisma/client-runtime-utils": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@prisma/client-runtime-utils/-/client-runtime-utils-7.2.0.tgz", + "integrity": "sha512-dn7oB53v0tqkB0wBdMuTNFNPdEbfICEUe82Tn9FoKAhJCUkDH+fmyEp0ClciGh+9Hp2Tuu2K52kth2MTLstvmA==", + "license": "Apache-2.0" + }, "node_modules/@prisma/config": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/@prisma/config/-/config-7.2.0.tgz", "integrity": "sha512-qmvSnfQ6l/srBW1S7RZGfjTQhc44Yl3ldvU6y3pgmuLM+83SBDs6UQVgMtQuMRe9J3gGqB0RF8wER6RlXEr6jQ==", - "dev": true, + "devOptional": true, "license": "Apache-2.0", "dependencies": { "c12": "3.1.0", @@ -730,14 +773,13 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-7.2.0.tgz", "integrity": "sha512-YSGTiSlBAVJPzX4ONZmMotL+ozJwQjRmZweQNIq/ER0tQJKJynNkRB3kyvt37eOfsbMCXk3gnLF6J9OJ4QWftw==", - "dev": true, "license": "Apache-2.0" }, "node_modules/@prisma/dev": { "version": "0.17.0", "resolved": "https://registry.npmjs.org/@prisma/dev/-/dev-0.17.0.tgz", "integrity": "sha512-6sGebe5jxX+FEsQTpjHLzvOGPn6ypFQprcs3jcuIWv1Xp/5v6P/rjfdvAwTkP2iF6pDx2tCd8vGLNWcsWzImTA==", - "dev": true, + "devOptional": true, "license": "ISC", "dependencies": { "@electric-sql/pglite": "0.3.2", @@ -759,11 +801,20 @@ "zeptomatch": "2.0.2" } }, + "node_modules/@prisma/driver-adapter-utils": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@prisma/driver-adapter-utils/-/driver-adapter-utils-7.2.0.tgz", + "integrity": "sha512-gzrUcbI9VmHS24Uf+0+7DNzdIw7keglJsD5m/MHxQOU68OhGVzlphQRobLiDMn8CHNA2XN8uugwKjudVtnfMVQ==", + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "7.2.0" + } + }, "node_modules/@prisma/engines": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-7.2.0.tgz", "integrity": "sha512-HUeOI/SvCDsHrR9QZn24cxxZcujOjcS3w1oW/XVhnSATAli5SRMOfp/WkG3TtT5rCxDA4xOnlJkW7xkho4nURA==", - "dev": true, + "devOptional": true, "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { @@ -777,14 +828,14 @@ "version": "7.2.0-4.0c8ef2ce45c83248ab3df073180d5eda9e8be7a3", "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-7.2.0-4.0c8ef2ce45c83248ab3df073180d5eda9e8be7a3.tgz", "integrity": "sha512-KezsjCZDsbjNR7SzIiVlUsn9PnLePI7r5uxABlwL+xoerurZTfgQVbIjvjF2sVr3Uc0ZcsnREw3F84HvbggGdA==", - "dev": true, + "devOptional": true, "license": "Apache-2.0" }, "node_modules/@prisma/engines/node_modules/@prisma/get-platform": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-7.2.0.tgz", "integrity": "sha512-k1V0l0Td1732EHpAfi2eySTezyllok9dXb6UQanajkJQzPUGi3vO2z7jdkz67SypFTdmbnyGYxvEvYZdZsMAVA==", - "dev": true, + "devOptional": true, "license": "Apache-2.0", "dependencies": { "@prisma/debug": "7.2.0" @@ -794,7 +845,7 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-7.2.0.tgz", "integrity": "sha512-Z5XZztJ8Ap+wovpjPD2lQKnB8nWFGNouCrglaNFjxIWAGWz0oeHXwUJRiclIoSSXN/ptcs9/behptSk8d0Yy6w==", - "dev": true, + "devOptional": true, "license": "Apache-2.0", "dependencies": { "@prisma/debug": "7.2.0", @@ -806,7 +857,7 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-7.2.0.tgz", "integrity": "sha512-k1V0l0Td1732EHpAfi2eySTezyllok9dXb6UQanajkJQzPUGi3vO2z7jdkz67SypFTdmbnyGYxvEvYZdZsMAVA==", - "dev": true, + "devOptional": true, "license": "Apache-2.0", "dependencies": { "@prisma/debug": "7.2.0" @@ -816,7 +867,7 @@ "version": "6.8.2", "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-6.8.2.tgz", "integrity": "sha512-vXSxyUgX3vm1Q70QwzwkjeYfRryIvKno1SXbIqwSptKwqKzskINnDUcx85oX+ys6ooN2ATGSD0xN2UTfg6Zcow==", - "dev": true, + "devOptional": true, "license": "Apache-2.0", "dependencies": { "@prisma/debug": "6.8.2" @@ -826,21 +877,21 @@ "version": "6.8.2", "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-6.8.2.tgz", "integrity": "sha512-4muBSSUwJJ9BYth5N8tqts8JtiLT8QI/RSAzEogwEfpbYGFo9mYsInsVo8dqXdPO2+Rm5OG5q0qWDDE3nyUbVg==", - "dev": true, + "devOptional": true, "license": "Apache-2.0" }, "node_modules/@prisma/query-plan-executor": { "version": "6.18.0", "resolved": "https://registry.npmjs.org/@prisma/query-plan-executor/-/query-plan-executor-6.18.0.tgz", "integrity": "sha512-jZ8cfzFgL0jReE1R10gT8JLHtQxjWYLiQ//wHmVYZ2rVkFHoh0DT8IXsxcKcFlfKN7ak7k6j0XMNn2xVNyr5cA==", - "dev": true, + "devOptional": true, "license": "Apache-2.0" }, "node_modules/@prisma/studio-core": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/@prisma/studio-core/-/studio-core-0.9.0.tgz", "integrity": "sha512-xA2zoR/ADu/NCSQuriBKTh6Ps4XjU0bErkEcgMfnSGh346K1VI7iWKnoq1l2DoxUqiddPHIEWwtxJ6xCHG6W7g==", - "dev": true, + "devOptional": true, "license": "Apache-2.0", "peerDependencies": { "@types/react": "^18.0.0 || ^19.0.0", @@ -852,7 +903,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/@types/node": { @@ -861,7 +912,6 @@ "integrity": "sha512-W609buLVRVmeW693xKfzHeIV6nJGGz98uCPfeXI1ELMLXVeKYZ9m15fAMSaUPBHYLGFsVRcMmSCksQOrZV9BYA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "undici-types": "~7.16.0" } @@ -870,7 +920,7 @@ "version": "19.2.7", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.7.tgz", "integrity": "sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==", - "dev": true, + "devOptional": true, "license": "MIT", "peer": true, "dependencies": { @@ -939,7 +989,7 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/aws-ssl-profiles/-/aws-ssl-profiles-1.1.2.tgz", "integrity": "sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">= 6.0.0" @@ -949,7 +999,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/c12/-/c12-3.1.0.tgz", "integrity": "sha512-uWoS8OU1MEIsOv8p/5a82c3H31LsWVR5qiyXVfBNOzfffjUWtPnhAb4BYI2uG2HfGmZmFjCtui5XNWaps+iFuw==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "chokidar": "^4.0.3", @@ -978,7 +1028,7 @@ "version": "16.6.1", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", - "dev": true, + "devOptional": true, "license": "BSD-2-Clause", "engines": { "node": ">=12" @@ -991,7 +1041,7 @@ "version": "10.5.0", "resolved": "https://registry.npmjs.org/chevrotain/-/chevrotain-10.5.0.tgz", "integrity": "sha512-Pkv5rBY3+CsHOYfV5g/Vs5JY9WTHHDEKOlohI2XeygaZhUeqhAlldZ8Hz9cRmxu709bvS08YzxHdTPHhffc13A==", - "dev": true, + "devOptional": true, "license": "Apache-2.0", "dependencies": { "@chevrotain/cst-dts-gen": "10.5.0", @@ -1006,7 +1056,7 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "readdirp": "^4.0.1" @@ -1022,7 +1072,7 @@ "version": "0.1.6", "resolved": "https://registry.npmjs.org/citty/-/citty-0.1.6.tgz", "integrity": "sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "consola": "^3.2.3" @@ -1032,14 +1082,14 @@ "version": "0.2.2", "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.2.tgz", "integrity": "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/consola": { "version": "3.4.2", "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": "^14.18.0 || >=16.10.0" @@ -1062,7 +1112,7 @@ "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "path-key": "^3.1.0", @@ -1077,14 +1127,14 @@ "version": "3.2.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", - "dev": true, + "devOptional": true, "license": "MIT" }, "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==", - "dev": true, + "devOptional": true, "license": "BSD-3-Clause", "engines": { "node": ">=16.0.0" @@ -1094,14 +1144,14 @@ "version": "6.1.4", "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.4.tgz", "integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/denque": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==", - "dev": true, + "devOptional": true, "license": "Apache-2.0", "engines": { "node": ">=0.10" @@ -1120,7 +1170,7 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/destr/-/destr-2.0.5.tgz", "integrity": "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/dotenv": { @@ -1140,7 +1190,7 @@ "version": "3.18.4", "resolved": "https://registry.npmjs.org/effect/-/effect-3.18.4.tgz", "integrity": "sha512-b1LXQJLe9D11wfnOKAk3PKxuqYshQ0Heez+y5pnkd3jLj1yx9QhM72zZ9uUrOQyNvrs2GZZd/3maL0ZV18YuDA==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "@standard-schema/spec": "^1.0.0", @@ -1151,7 +1201,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/empathic/-/empathic-2.0.0.tgz", "integrity": "sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">=14" @@ -1203,14 +1253,14 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.8.tgz", "integrity": "sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA==", - "dev": true, + "devOptional": true, "license": "MIT" }, "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==", - "dev": true, + "devOptional": true, "funding": [ { "type": "individual", @@ -1366,7 +1416,7 @@ "version": "3.3.1", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", - "dev": true, + "devOptional": true, "license": "ISC", "dependencies": { "cross-spawn": "^7.0.6", @@ -1398,7 +1448,7 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz", "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "is-property": "^1.0.2" @@ -1408,7 +1458,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/get-port-please/-/get-port-please-3.1.2.tgz", "integrity": "sha512-Gxc29eLs1fbn6LQ4jSU4vXjlwyZhF5HsGuMAa7gqBP4Rw4yxxltyDUuF5MBclFzDTXO+ACchGQoeela4DSfzdQ==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/get-tsconfig": { @@ -1428,7 +1478,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/giget/-/giget-2.0.0.tgz", "integrity": "sha512-L5bGsVkxJbJgdnwyuheIunkGatUF/zssUoxxjACCseZYAVbaqdh9Tsmmlkl8vYan09H7sbvKt4pS8GqKLBrEzA==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "citty": "^0.1.6", @@ -1446,21 +1496,21 @@ "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true, + "devOptional": true, "license": "ISC" }, "node_modules/grammex": { "version": "3.1.12", "resolved": "https://registry.npmjs.org/grammex/-/grammex-3.1.12.tgz", "integrity": "sha512-6ufJOsSA7LcQehIJNCO7HIBykfM7DXQual0Ny780/DEcJIpBlHRvcqEBWGPYd7hrXL2GJ3oJI1MIhaXjWmLQOQ==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/hono": { "version": "4.10.6", "resolved": "https://registry.npmjs.org/hono/-/hono-4.10.6.tgz", "integrity": "sha512-BIdolzGpDO9MQ4nu3AUuDwHZZ+KViNm+EZ75Ae55eMXMqLVhDFqEMXxtUe9Qh8hjL+pIna/frs2j6Y2yD5Ua/g==", - "dev": true, + "devOptional": true, "license": "MIT", "peer": true, "engines": { @@ -1471,14 +1521,14 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/http-status-codes/-/http-status-codes-2.3.0.tgz", "integrity": "sha512-RJ8XvFvpPM/Dmc5SV+dC4y5PCeOhT3x1Hq0NU3rjGeg5a/CqlhZ7uudknPwZFz4aeAXDcbAyaeP7GAo9lvngtA==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/iconv-lite": { "version": "0.7.1", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.1.tgz", "integrity": "sha512-2Tth85cXwGFHfvRgZWszZSvdo+0Xsqmw8k8ZwxScfcBneNUraK+dxRxRm24nszx80Y0TVio8kKLt5sLE7ZCLlw==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" @@ -1504,21 +1554,21 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", "integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, + "devOptional": true, "license": "ISC" }, "node_modules/jiti": { "version": "2.6.1", "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", - "dev": true, + "devOptional": true, "license": "MIT", "bin": { "jiti": "lib/jiti-cli.mjs" @@ -1599,7 +1649,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">=10" @@ -1609,21 +1659,21 @@ "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/long": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", - "dev": true, + "devOptional": true, "license": "Apache-2.0" }, "node_modules/lru.min": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/lru.min/-/lru.min-1.1.3.tgz", "integrity": "sha512-Lkk/vx6ak3rYkRR0Nhu4lFUT2VDnQSxBe8Hbl7f36358p6ow8Bnvr8lrLt98H8J1aGxfhbX4Fs5tYg2+FTwr5Q==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "bun": ">=1.0.0", @@ -1639,7 +1689,7 @@ "version": "3.15.3", "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.15.3.tgz", "integrity": "sha512-FBrGau0IXmuqg4haEZRBfHNWB5mUARw6hNwPDXXGg0XzVJ50mr/9hb267lvpVMnhZ1FON3qNd4Xfcez1rbFwSg==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "aws-ssl-profiles": "^1.1.1", @@ -1660,7 +1710,7 @@ "version": "1.1.6", "resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.6.tgz", "integrity": "sha512-Tz09sEL2EEuv5fFowm419c1+a/jSMiBjI9gHxVLrVdbUkkNUUfjsVYs9pVZu5oCon/kmRh9TfLEObFtkVxmY0w==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "lru.min": "^1.1.0" @@ -1673,14 +1723,14 @@ "version": "1.6.7", "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.7.tgz", "integrity": "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/nypm": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/nypm/-/nypm-0.6.2.tgz", "integrity": "sha512-7eM+hpOtrKrBDCh7Ypu2lJ9Z7PNZBdi/8AT3AX8xoCj43BBVHD0hPSTEvMtkMpfs8FCqBGhxB+uToIQimA111g==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "citty": "^0.1.6", @@ -1700,7 +1750,7 @@ "version": "2.0.11", "resolved": "https://registry.npmjs.org/ohash/-/ohash-2.0.11.tgz", "integrity": "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/on-exit-leak-free": { @@ -1716,7 +1766,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">=8" @@ -1726,16 +1776,115 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", - "dev": true, + "devOptional": true, "license": "MIT" }, "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==", - "dev": true, + "devOptional": true, + "license": "MIT" + }, + "node_modules/pg": { + "version": "8.16.3", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.16.3.tgz", + "integrity": "sha512-enxc1h0jA/aq5oSDMvqyW3q89ra6XIIDZgCX9vkMrnz5DFTw/Ny3Li2lFQ+pt3L6MCgm/5o2o8HW9hiJji+xvw==", + "license": "MIT", + "peer": true, + "dependencies": { + "pg-connection-string": "^2.9.1", + "pg-pool": "^3.10.1", + "pg-protocol": "^1.10.3", + "pg-types": "2.2.0", + "pgpass": "1.0.5" + }, + "engines": { + "node": ">= 16.0.0" + }, + "optionalDependencies": { + "pg-cloudflare": "^1.2.7" + }, + "peerDependencies": { + "pg-native": ">=3.0.1" + }, + "peerDependenciesMeta": { + "pg-native": { + "optional": true + } + } + }, + "node_modules/pg-cloudflare": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.2.7.tgz", + "integrity": "sha512-YgCtzMH0ptvZJslLM1ffsY4EuGaU0cx4XSdXLRFae8bPP4dS5xL1tNB3k2o/N64cHJpwU7dxKli/nZ2lUa5fLg==", + "license": "MIT", + "optional": true + }, + "node_modules/pg-connection-string": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.9.1.tgz", + "integrity": "sha512-nkc6NpDcvPVpZXxrreI/FOtX3XemeLl8E0qFr6F2Lrm/I8WOnaWNhIPK2Z7OHpw7gh5XJThi6j6ppgNoaT1w4w==", + "license": "MIT" + }, + "node_modules/pg-int8": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", + "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", + "license": "ISC", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/pg-pool": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.10.1.tgz", + "integrity": "sha512-Tu8jMlcX+9d8+QVzKIvM/uJtp07PKr82IUOYEphaWcoBhIYkoHpLXN3qO59nAI11ripznDsEzEv8nUxBVWajGg==", + "license": "MIT", + "peerDependencies": { + "pg": ">=8.0" + } + }, + "node_modules/pg-protocol": { + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.10.3.tgz", + "integrity": "sha512-6DIBgBQaTKDJyxnXaLiLR8wBpQQcGWuAESkRBX/t6OwA8YsqP+iVSiond2EDy6Y/dsGk8rh/jtax3js5NeV7JQ==", "license": "MIT" }, + "node_modules/pg-types": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", + "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", + "license": "MIT", + "dependencies": { + "pg-int8": "1.0.1", + "postgres-array": "~2.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.4", + "postgres-interval": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pg-types/node_modules/postgres-array": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", + "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/pgpass": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", + "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", + "license": "MIT", + "dependencies": { + "split2": "^4.1.0" + } + }, "node_modules/pino": { "version": "10.1.0", "resolved": "https://registry.npmjs.org/pino/-/pino-10.1.0.tgz", @@ -1777,7 +1926,7 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.3.0.tgz", "integrity": "sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "confbox": "^0.2.2", @@ -1789,7 +1938,7 @@ "version": "3.4.7", "resolved": "https://registry.npmjs.org/postgres/-/postgres-3.4.7.tgz", "integrity": "sha512-Jtc2612XINuBjIl/QTWsV5UvE8UHuNblcO3vVADSrKsrc6RqGX6lOW1cEo3CM2v0XG4Nat8nI+YM7/f26VxXLw==", - "dev": true, + "devOptional": true, "license": "Unlicense", "engines": { "node": ">=12" @@ -1799,13 +1948,53 @@ "url": "https://github.com/sponsors/porsager" } }, + "node_modules/postgres-array": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-3.0.4.tgz", + "integrity": "sha512-nAUSGfSDGOaOAEGwqsRY27GPOea7CNipJPOA7lPbdEpx5Kg3qzdP0AaWC5MlhTWV9s4hFX39nomVZ+C4tnGOJQ==", + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/postgres-bytea": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.1.tgz", + "integrity": "sha512-5+5HqXnsZPE65IJZSMkZtURARZelel2oXUEO8rH83VS/hxH5vv1uHquPg5wZs8yMAfdv971IU+kcPUczi7NVBQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-date": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", + "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-interval": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", + "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", + "license": "MIT", + "dependencies": { + "xtend": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/prisma": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/prisma/-/prisma-7.2.0.tgz", "integrity": "sha512-jSdHWgWOgFF24+nRyyNRVBIgGDQEsMEF8KPHvhBBg3jWyR9fUAK0Nq9ThUmiGlNgq2FA7vSk/ZoCvefod+a8qg==", - "dev": true, + "devOptional": true, "hasInstallScript": true, "license": "Apache-2.0", + "peer": true, "dependencies": { "@prisma/config": "7.2.0", "@prisma/dev": "0.17.0", @@ -1853,7 +2042,7 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz", "integrity": "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "graceful-fs": "^4.2.4", @@ -1865,14 +2054,14 @@ "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true, + "devOptional": true, "license": "ISC" }, "node_modules/pure-rand": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", - "dev": true, + "devOptional": true, "funding": [ { "type": "individual", @@ -1895,7 +2084,7 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/rc9/-/rc9-2.1.2.tgz", "integrity": "sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "defu": "^6.1.4", @@ -1906,7 +2095,7 @@ "version": "19.2.3", "resolved": "https://registry.npmjs.org/react/-/react-19.2.3.tgz", "integrity": "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==", - "dev": true, + "devOptional": true, "license": "MIT", "peer": true, "engines": { @@ -1917,7 +2106,7 @@ "version": "19.2.3", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.3.tgz", "integrity": "sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==", - "dev": true, + "devOptional": true, "license": "MIT", "peer": true, "dependencies": { @@ -1931,7 +2120,7 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">= 14.18.0" @@ -1954,14 +2143,14 @@ "version": "0.5.0", "resolved": "https://registry.npmjs.org/regexp-to-ast/-/regexp-to-ast-0.5.0.tgz", "integrity": "sha512-tlbJqcMHnPKI9zSrystikWKwHkBqu2a/Sgw01h3zFjvYrMxEDYHzzoMZnUrbIfpTFEsoRnnviOXNCzFiSc54Qw==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/remeda": { "version": "2.21.3", "resolved": "https://registry.npmjs.org/remeda/-/remeda-2.21.3.tgz", "integrity": "sha512-XXrZdLA10oEOQhLLzEJEiFFSKi21REGAkHdImIb4rt/XXy8ORGXh5HCcpUOsElfPNDb+X6TA/+wkh+p2KffYmg==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "type-fest": "^4.39.1" @@ -1999,7 +2188,7 @@ "version": "0.12.0", "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">= 4" @@ -2053,14 +2242,14 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/scheduler": { "version": "0.27.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/secure-json-parse": { @@ -2095,7 +2284,7 @@ "version": "0.0.5", "resolved": "https://registry.npmjs.org/seq-queue/-/seq-queue-0.0.5.tgz", "integrity": "sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q==", - "dev": true + "devOptional": true }, "node_modules/set-cookie-parser": { "version": "2.7.2", @@ -2107,7 +2296,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "shebang-regex": "^3.0.0" @@ -2120,7 +2309,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">=8" @@ -2130,7 +2319,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, + "devOptional": true, "license": "ISC", "engines": { "node": ">=14" @@ -2161,7 +2350,7 @@ "version": "2.3.3", "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.3.tgz", "integrity": "sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">= 0.6" @@ -2171,7 +2360,7 @@ "version": "3.9.0", "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.9.0.tgz", "integrity": "sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/thread-stream": { @@ -2187,7 +2376,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz", "integrity": "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">=18" @@ -2226,7 +2415,7 @@ "version": "4.41.0", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", - "dev": true, + "devOptional": true, "license": "(MIT OR CC0-1.0)", "engines": { "node": ">=16" @@ -2239,7 +2428,7 @@ "version": "5.9.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", - "dev": true, + "devOptional": true, "license": "Apache-2.0", "peer": true, "bin": { @@ -2261,7 +2450,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/valibot/-/valibot-1.2.0.tgz", "integrity": "sha512-mm1rxUsmOxzrwnX5arGS+U4T25RdvpPjPN4yR0u9pUBov9+zGVtO84tif1eY4r6zWxVxu3KzIyknJy3rxfRZZg==", - "dev": true, + "devOptional": true, "license": "MIT", "peerDependencies": { "typescript": ">=5" @@ -2276,7 +2465,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, + "devOptional": true, "license": "ISC", "dependencies": { "isexe": "^2.0.0" @@ -2288,11 +2477,20 @@ "node": ">= 8" } }, + "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/zeptomatch": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/zeptomatch/-/zeptomatch-2.0.2.tgz", "integrity": "sha512-H33jtSKf8Ijtb5BW6wua3G5DhnFjbFML36eFu+VdOoVY4HD9e7ggjqdM6639B+L87rjnR6Y+XeRzBXZdy52B/g==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "grammex": "^3.1.10" diff --git a/backend/package.json b/backend/package.json index 7c24c73..76fea16 100644 --- a/backend/package.json +++ b/backend/package.json @@ -16,6 +16,8 @@ "dependencies": { "@fastify/autoload": "^6.3.1", "@fastify/cors": "^11.2.0", + "@prisma/adapter-pg": "^7.2.0", + "@prisma/client": "^7.2.0", "fastify": "^5.6.2", "jose": "^6.1.3", "zod": "^4.2.1" diff --git a/backend/prisma.config.ts b/backend/prisma.config.ts index 831a20f..b66a42e 100644 --- a/backend/prisma.config.ts +++ b/backend/prisma.config.ts @@ -1,5 +1,3 @@ -// This file was generated by Prisma, and assumes you have installed the following: -// npm install --save-dev prisma dotenv import "dotenv/config"; import { defineConfig } from "prisma/config"; @@ -8,7 +6,8 @@ export default defineConfig({ migrations: { path: "prisma/migrations", }, + // @ts-ignore datasource: { - url: process.env["DATABASE_URL"], + url: process.env.DATABASE_URL, }, }); diff --git a/backend/prisma/migrations/20251226152623_init/migration.sql b/backend/prisma/migrations/20251226152623_init/migration.sql new file mode 100644 index 0000000..24b7c4d --- /dev/null +++ b/backend/prisma/migrations/20251226152623_init/migration.sql @@ -0,0 +1,28 @@ +-- CreateTable +CREATE TABLE "Organiser" ( + "id" SERIAL NOT NULL, + "telegram_id" INTEGER NOT NULL, + + CONSTRAINT "Organiser_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "QueueConfig" ( + "id" INTEGER NOT NULL, + "positionBeforePing" INTEGER NOT NULL, + "isOpen" BOOLEAN NOT NULL, + + CONSTRAINT "QueueConfig_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "Queue" ( + "telegram_id" INTEGER NOT NULL, + "timeCreated" TIMESTAMP(3) NOT NULL +); + +-- CreateIndex +CREATE UNIQUE INDEX "Organiser_telegram_id_key" ON "Organiser"("telegram_id"); + +-- CreateIndex +CREATE UNIQUE INDEX "Queue_telegram_id_key" ON "Queue"("telegram_id"); diff --git a/backend/prisma/migrations/migration_lock.toml b/backend/prisma/migrations/migration_lock.toml new file mode 100644 index 0000000..044d57c --- /dev/null +++ b/backend/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/backend/prisma/schema.prisma b/backend/prisma/schema.prisma index f51cc2c..ba38f8e 100644 --- a/backend/prisma/schema.prisma +++ b/backend/prisma/schema.prisma @@ -1,9 +1,3 @@ -// 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" output = "../src/generated/prisma" @@ -12,3 +6,19 @@ generator client { datasource db { provider = "postgresql" } + +model Organiser { + id Int @id @default(autoincrement()) + telegram_id Int @unique +} + +model QueueConfig { + id Int @id + positionBeforePing Int + isOpen Boolean +} + +model Queue { + telegram_id Int @unique + timeCreated DateTime +} diff --git a/backend/src/prismaPlugin.ts b/backend/src/prismaPlugin.ts new file mode 100644 index 0000000..e5ee789 --- /dev/null +++ b/backend/src/prismaPlugin.ts @@ -0,0 +1,31 @@ +import fp from 'fastify-plugin' +import type {FastifyPluginAsync} from 'fastify' +import {PrismaClient} from "./generated/prisma/client.js"; +import { PrismaPg } from '@prisma/adapter-pg' + +declare module 'fastify' { + interface FastifyInstance { + prisma: PrismaClient + } +} + +const prismaPlugin: FastifyPluginAsync = fp(async (fastify, options) => { + + const adapter = new PrismaPg({ connectionString: process.env.DATABASE_URL }) + + const prisma = new PrismaClient({ + log: [{ level: 'error', emit: 'event' }], + adapter: adapter, + }); + + await prisma.$connect() + + // make prisma client accessible through fastify instance + fastify.decorate('prisma', prisma) + + fastify.addHook('onClose', async (fastify) => { + await fastify.prisma.$disconnect() + }) +}) + +export default prismaPlugin \ No newline at end of file diff --git a/backend/src/routes/public/index.ts b/backend/src/routes/public/index.ts new file mode 100644 index 0000000..5113263 --- /dev/null +++ b/backend/src/routes/public/index.ts @@ -0,0 +1,12 @@ +import type {FastifyPluginAsync} from "fastify"; + +const route: FastifyPluginAsync = async (fastify, _) => { + + fastify.get('/', async (request, reply) => { + // get number of organizers + fastify.prisma.organiser.count().then((count) => console.log(count)); + }); + +}; + +export default route; \ No newline at end of file diff --git a/backend/src/server.ts b/backend/src/server.ts index 7ca4bdf..92a4d38 100644 --- a/backend/src/server.ts +++ b/backend/src/server.ts @@ -3,6 +3,7 @@ import cors from '@fastify/cors' import * as jose from 'jose' import autoLoad from '@fastify/autoload'; import { join } from 'path'; +import prismaPlugin from './prismaPlugin.js'; const JWT_SECRET = process.env.JWT_SECRET!; @@ -38,6 +39,9 @@ const authHook = async (request: FastifyRequest, reply: FastifyReply) => { }; +// Init sql db connection +fastify.register(prismaPlugin); + fastify.register((fastify) => { fastify.addHook("preHandler", authHook); From dedce2c18548fc0f282c7315f3d12b2e7680ac2a Mon Sep 17 00:00:00 2001 From: jaxon Date: Sat, 27 Dec 2025 12:53:14 +0800 Subject: [PATCH 03/19] set up zod type validation --- backend/package-lock.json | 104 +++++++++++++++++++++++++++++++++++ backend/package.json | 3 +- backend/prisma/schema.prisma | 2 +- backend/src/server.ts | 18 +++++- 4 files changed, 123 insertions(+), 4 deletions(-) diff --git a/backend/package-lock.json b/backend/package-lock.json index 3ff3d40..751359d 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -14,6 +14,7 @@ "@prisma/adapter-pg": "^7.2.0", "@prisma/client": "^7.2.0", "fastify": "^5.6.2", + "fastify-type-provider-zod": "^6.1.0", "jose": "^6.1.3", "zod": "^4.2.1" }, @@ -682,6 +683,30 @@ "ipaddr.js": "^2.1.0" } }, + "node_modules/@fastify/swagger": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/@fastify/swagger/-/swagger-9.6.1.tgz", + "integrity": "sha512-fKlpJqFMWoi4H3EdUkDaMteEYRCfQMEkK0HJJ0eaf4aRlKd8cbq0pVkOfXDXmtvMTXYcnx3E+l023eFDBsA1HA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "fastify-plugin": "^5.0.0", + "json-schema-resolver": "^3.0.0", + "openapi-types": "^12.1.3", + "rfdc": "^1.3.1", + "yaml": "^2.4.2" + } + }, "node_modules/@hono/node-server": { "version": "1.19.6", "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.6.tgz", @@ -1130,6 +1155,23 @@ "devOptional": true, "license": "MIT" }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "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", @@ -1355,6 +1397,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "@fastify/ajv-compiler": "^4.0.0", "@fastify/error": "^4.0.0", @@ -1389,6 +1432,21 @@ ], "license": "MIT" }, + "node_modules/fastify-type-provider-zod": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/fastify-type-provider-zod/-/fastify-type-provider-zod-6.1.0.tgz", + "integrity": "sha512-Sl19VZFSX4W/+AFl3hkL5YgWk3eDXZ4XYOdrq94HunK+o7GQBCAqgk7+3gPXoWkF0bNxOiIgfnFGJJ3i9a2BtQ==", + "license": "MIT", + "dependencies": { + "@fastify/error": "^4.2.0" + }, + "peerDependencies": { + "@fastify/swagger": ">=9.5.1", + "fastify": "^5.5.0", + "openapi-types": "^12.1.3", + "zod": ">=4.1.5" + } + }, "node_modules/fastq": { "version": "1.19.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", @@ -1602,6 +1660,23 @@ "dequal": "^2.0.3" } }, + "node_modules/json-schema-resolver": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/json-schema-resolver/-/json-schema-resolver-3.0.0.tgz", + "integrity": "sha512-HqMnbz0tz2DaEJ3ntsqtx3ezzZyDE7G56A/pPY/NGmrPu76UzsWquOpHFRAf5beTNXoH2LU5cQePVvRli1nchA==", + "license": "MIT", + "dependencies": { + "debug": "^4.1.1", + "fast-uri": "^3.0.5", + "rfdc": "^1.1.4" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/Eomm/json-schema-resolver?sponsor=1" + } + }, "node_modules/json-schema-traverse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", @@ -1685,6 +1760,12 @@ "url": "https://github.com/sponsors/wellwelwel" } }, + "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/mysql2": { "version": "3.15.3", "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.15.3.tgz", @@ -1762,6 +1843,13 @@ "node": ">=14.0.0" } }, + "node_modules/openapi-types": { + "version": "12.1.3", + "resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-12.1.3.tgz", + "integrity": "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==", + "license": "MIT", + "peer": true + }, "node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", @@ -2486,6 +2574,21 @@ "node": ">=0.4" } }, + "node_modules/yaml": { + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.2.tgz", + "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==", + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + }, "node_modules/zeptomatch": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/zeptomatch/-/zeptomatch-2.0.2.tgz", @@ -2501,6 +2604,7 @@ "resolved": "https://registry.npmjs.org/zod/-/zod-4.2.1.tgz", "integrity": "sha512-0wZ1IRqGGhMP76gLqz8EyfBXKk0J2qo2+H3fi4mcUP/KtTocoX08nmIAHl1Z2kJIZbZee8KOpBCSNPRgauucjw==", "license": "MIT", + "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } diff --git a/backend/package.json b/backend/package.json index 76fea16..8030dcc 100644 --- a/backend/package.json +++ b/backend/package.json @@ -4,7 +4,7 @@ "description": "", "main": "src/server.ts", "scripts": { - "dev": "tsx --env-file=.env src/server.ts", + "dev": "tsx watch --env-file=.env src/server.ts", "build": "tsc", "start": "node dist/server.js", "test": "echo \"Error: no test specified\" && exit 1" @@ -19,6 +19,7 @@ "@prisma/adapter-pg": "^7.2.0", "@prisma/client": "^7.2.0", "fastify": "^5.6.2", + "fastify-type-provider-zod": "^6.1.0", "jose": "^6.1.3", "zod": "^4.2.1" }, diff --git a/backend/prisma/schema.prisma b/backend/prisma/schema.prisma index ba38f8e..e37a1cb 100644 --- a/backend/prisma/schema.prisma +++ b/backend/prisma/schema.prisma @@ -13,7 +13,7 @@ model Organiser { } model QueueConfig { - id Int @id + id Int @id @default(autoincrement()) positionBeforePing Int isOpen Boolean } diff --git a/backend/src/server.ts b/backend/src/server.ts index 92a4d38..90fabcf 100644 --- a/backend/src/server.ts +++ b/backend/src/server.ts @@ -4,12 +4,21 @@ import * as jose from 'jose' import autoLoad from '@fastify/autoload'; import { join } from 'path'; import prismaPlugin from './prismaPlugin.js'; +import { + validatorCompiler, + serializerCompiler, + type ZodTypeProvider +} from 'fastify-type-provider-zod'; const JWT_SECRET = process.env.JWT_SECRET!; const fastify = Fastify({ - logger: true -}); + logger: process.env.NODE_ENV === "development", +}).withTypeProvider(); + +// tell fastify to use zod for type validation +fastify.setValidatorCompiler(validatorCompiler); +fastify.setSerializerCompiler(serializerCompiler); await fastify.register(cors, { origin: true, @@ -20,6 +29,11 @@ await fastify.register(cors, { const authHook = async (request: FastifyRequest, reply: FastifyReply) => { + // bypass auth check during development + if (process.env.NODE_ENV === 'development') { + return + } + const secret = new TextEncoder().encode( JWT_SECRET ); From 321fc0359ce84115cf9bfc384d44eb2f69bc084c Mon Sep 17 00:00:00 2001 From: jaxon Date: Sat, 27 Dec 2025 15:06:45 +0800 Subject: [PATCH 04/19] added hook for admin check --- backend/prisma/schema.prisma | 6 +++--- backend/src/routes/private/queue/hello.ts | 14 -------------- backend/src/routes/public/auth/index.ts | 5 +++++ backend/src/server.ts | 12 +++++------- backend/src/shared.ts | 14 ++++++++++++++ backend/src/types.ts | 2 +- 6 files changed, 28 insertions(+), 25 deletions(-) delete mode 100644 backend/src/routes/private/queue/hello.ts create mode 100644 backend/src/shared.ts diff --git a/backend/prisma/schema.prisma b/backend/prisma/schema.prisma index e37a1cb..543fe69 100644 --- a/backend/prisma/schema.prisma +++ b/backend/prisma/schema.prisma @@ -8,8 +8,8 @@ datasource db { } model Organiser { - id Int @id @default(autoincrement()) - telegram_id Int @unique + id Int @id @default(autoincrement()) + telegram_id BigInt @unique } model QueueConfig { @@ -19,6 +19,6 @@ model QueueConfig { } model Queue { - telegram_id Int @unique + telegram_id BigInt @unique timeCreated DateTime } diff --git a/backend/src/routes/private/queue/hello.ts b/backend/src/routes/private/queue/hello.ts deleted file mode 100644 index dc2585e..0000000 --- a/backend/src/routes/private/queue/hello.ts +++ /dev/null @@ -1,14 +0,0 @@ -import type {FastifyPluginAsync} from 'fastify'; - - -const route: FastifyPluginAsync = async (fastify, _) => { - - fastify.get('/hello', async (request, reply) => { - - return reply.code(200).send(request.userId); - - }); - -}; - -export default route; \ No newline at end of file diff --git a/backend/src/routes/public/auth/index.ts b/backend/src/routes/public/auth/index.ts index 1263156..c4de0f4 100644 --- a/backend/src/routes/public/auth/index.ts +++ b/backend/src/routes/public/auth/index.ts @@ -69,6 +69,11 @@ const route: FastifyPluginAsync = async (fastify, _) => { fastify.get('/', async (request, reply) => { + // Generate a valid JWT for development purpose + if (process.env.NODE_ENV === 'development') { + return reply.code(200).send({token: await newJWT(2202843044)}); + } + const [authType, authData = ''] = (request.headers.authorization || '').split(' '); switch (authType) { diff --git a/backend/src/server.ts b/backend/src/server.ts index 90fabcf..26f3126 100644 --- a/backend/src/server.ts +++ b/backend/src/server.ts @@ -29,19 +29,17 @@ await fastify.register(cors, { const authHook = async (request: FastifyRequest, reply: FastifyReply) => { - // bypass auth check during development - if (process.env.NODE_ENV === 'development') { - return - } - const secret = new TextEncoder().encode( JWT_SECRET ); try { - if (request.headers.authorization === undefined) { - return reply.code(400).send({"error": "Missing JWT token"}); + // bypass auth check during development + if (request.headers.authorization == undefined) { + if (process.env.NODE_ENV !== 'development') { + return reply.code(400).send({"error": "Missing JWT token"}); + } } const { payload } = await jose.jwtVerify(request.headers.authorization!, secret); diff --git a/backend/src/shared.ts b/backend/src/shared.ts new file mode 100644 index 0000000..15d9b7c --- /dev/null +++ b/backend/src/shared.ts @@ -0,0 +1,14 @@ +import {type FastifyReply, type FastifyRequest} from "fastify"; + +export async function isAdmin(request: FastifyRequest, reply: FastifyReply) { + + if (request.userId == null) { + return reply.code(401).send({"error": "Missing user id"}); + } + + await request.server.prisma.organiser.findFirst({where: {telegram_id: request.userId!}}).then((user) => { + if (user == null) { + return reply.code(401).send({"error": "Endpoint requires administrator privileges"}); + } + }); +} \ No newline at end of file diff --git a/backend/src/types.ts b/backend/src/types.ts index 584777f..006fc86 100644 --- a/backend/src/types.ts +++ b/backend/src/types.ts @@ -2,7 +2,7 @@ import 'fastify'; declare module 'fastify' { interface FastifyRequest { - userId: number; + userId: number | undefined; } } From aaf701d5cf7345d1ecc65c98d7087c03df9f0626 Mon Sep 17 00:00:00 2001 From: jaxon Date: Sat, 27 Dec 2025 15:28:56 +0800 Subject: [PATCH 05/19] added endpoint for querying and updating queue status --- backend/README.md | 34 +++++++++++++-- backend/src/routes/private/queue/status.ts | 50 ++++++++++++++++++++++ backend/src/shared.ts | 2 +- 3 files changed, 81 insertions(+), 5 deletions(-) create mode 100644 backend/src/routes/private/queue/status.ts diff --git a/backend/README.md b/backend/README.md index 7b644b2..aadca5b 100644 --- a/backend/README.md +++ b/backend/README.md @@ -34,8 +34,34 @@ npm run dev JWT check is toggled based on directory -| Directory | Access Level | Description | -| :--- | :--- |:----------------------------------------| -| `src/routes/public` | **Unprotected** | Open endpoints like `/auth`. | -| `src/routes/private` | **Authenticated** | Requires a valid JWT to access. | +| Directory | Access Level | Description | +|:---------------------|:------------------|:--------------------------------| +| `src/routes/public` | **Unprotected** | Open endpoints like `/auth`. | +| `src/routes/private` | **Authenticated** | Requires a valid JWT to access. | +## Endpoints Documentation + +### `/queue/status` +To check if queue is opened or closed. +- **Method:** `GET` +- **Auth:** Require +- **Success (200 OK):** `{ "status": boolean }` +- **Possible Errors:** + + | Code | Error Message | Reason | + |:-----|:----------------------|:---------------------------------| + | 500 | `No queue configured` | No queue exists in the database. | + +### `/queue/status` +Opens or closes the queue. +- **Method:** `PATCH` +- **Auth:** Require (Admin) +- **Query Params:** `open=` +- **Success (200 OK):** Returns the full updated `QueueConfig` object. +- **Possible Errors:** + + | Code | Error Message | Reason | + |:-----|:--------------|:--------------------------------------------| + | 400 | | invalid query parameters | + | 403 | `Forbidden` | User is not an admin. | + | 500 | | Failed to write the update to the database. | \ No newline at end of file diff --git a/backend/src/routes/private/queue/status.ts b/backend/src/routes/private/queue/status.ts new file mode 100644 index 0000000..cc63139 --- /dev/null +++ b/backend/src/routes/private/queue/status.ts @@ -0,0 +1,50 @@ +import {z} from 'zod'; +import type {FastifyPluginAsyncZod} from "fastify-type-provider-zod"; +import {isAdmin} from "../../../shared.js"; + + +const route: FastifyPluginAsyncZod = async (fastify, _) => { + + // get queue status + fastify.get('/status', async (request, reply) => { + await fastify.prisma.queueConfig.findFirst().then((queueConfig) => { + if (queueConfig === null) { + return reply.code(500).send({error: 'No queue configured'}); + } + return reply.code(200).send({status: queueConfig.isOpen}); + }) + + }); + + // update queue status + fastify.patch('/status', { + preHandler: isAdmin, + schema: { + querystring: z.object({ + open: z.coerce.boolean(), + })} + }, async (request, reply) => { + let config = await fastify.prisma.queueConfig.findFirst().then((queueConfig) => queueConfig); + + if (config === null) { + return reply.code(500).send({error: 'No queue configured'}); + } + + config.isOpen = request.query.open; + + await fastify.prisma.queueConfig.update({ + where: { + id: config.id + }, + data: config + }).then((config) => { + return reply.code(200).send(config); + }).catch((err) => { + return reply.code(500).send({error: err.message}); + }); + + }); + +}; + +export default route; \ No newline at end of file diff --git a/backend/src/shared.ts b/backend/src/shared.ts index 15d9b7c..b8313ea 100644 --- a/backend/src/shared.ts +++ b/backend/src/shared.ts @@ -8,7 +8,7 @@ export async function isAdmin(request: FastifyRequest, reply: FastifyReply) { await request.server.prisma.organiser.findFirst({where: {telegram_id: request.userId!}}).then((user) => { if (user == null) { - return reply.code(401).send({"error": "Endpoint requires administrator privileges"}); + return reply.code(403).send({"error": "Endpoint requires administrator privileges"}); } }); } \ No newline at end of file From 073d0debe70ea17cb0db299dd53799e9b6a7864e Mon Sep 17 00:00:00 2001 From: jaxon Date: Sun, 28 Dec 2025 11:02:16 +0800 Subject: [PATCH 06/19] added new adminRequester table and renamed organiser table to admin --- backend/prisma/schema.prisma | 8 +++++- backend/src/routes/private/admins/index.ts | 31 ++++++++++++++++++++++ backend/src/routes/public/index.ts | 6 +++-- backend/src/server.ts | 2 ++ 4 files changed, 44 insertions(+), 3 deletions(-) create mode 100644 backend/src/routes/private/admins/index.ts diff --git a/backend/prisma/schema.prisma b/backend/prisma/schema.prisma index 543fe69..929f3ea 100644 --- a/backend/prisma/schema.prisma +++ b/backend/prisma/schema.prisma @@ -7,7 +7,7 @@ datasource db { provider = "postgresql" } -model Organiser { +model Admin { id Int @id @default(autoincrement()) telegram_id BigInt @unique } @@ -22,3 +22,9 @@ model Queue { telegram_id BigInt @unique timeCreated DateTime } + +model AdminRequester { + id Int @id @default(autoincrement()) + telegram_id BigInt @unique + telegram_username String +} diff --git a/backend/src/routes/private/admins/index.ts b/backend/src/routes/private/admins/index.ts new file mode 100644 index 0000000..bd3474e --- /dev/null +++ b/backend/src/routes/private/admins/index.ts @@ -0,0 +1,31 @@ +import {isAdmin} from "../../../shared.js"; +import type {FastifyPluginAsyncZod} from "fastify-type-provider-zod"; +import {z} from 'zod'; + +const route: FastifyPluginAsyncZod = async (fastify, _) => { + + fastify.get('/', {preHandler: isAdmin}, async (request, reply) => { + await fastify.prisma.admin.findMany().then((admins) => { + return reply.code(200).send(admins); + }).catch((err) => { + return reply.code(500).send({error: err.message}); + }); + }); + + fastify.post('/:targetId', { + schema: { + params: z.object({ + targetId: z.coerce.number() + }), + }, + preHandler: isAdmin}, async (request, reply) => { + + const {targetId} = request.params; + + + + }); + +}; + +export default route; \ No newline at end of file diff --git a/backend/src/routes/public/index.ts b/backend/src/routes/public/index.ts index 5113263..46960bf 100644 --- a/backend/src/routes/public/index.ts +++ b/backend/src/routes/public/index.ts @@ -1,10 +1,12 @@ import type {FastifyPluginAsync} from "fastify"; +// dummy example endpoint +// TODO: remove later const route: FastifyPluginAsync = async (fastify, _) => { fastify.get('/', async (request, reply) => { - // get number of organizers - fastify.prisma.organiser.count().then((count) => console.log(count)); + // get number of admins + fastify.prisma.admin.count().then((count) => console.log(count)); }); }; diff --git a/backend/src/server.ts b/backend/src/server.ts index 26f3126..4798327 100644 --- a/backend/src/server.ts +++ b/backend/src/server.ts @@ -60,6 +60,7 @@ fastify.register((fastify) => { fastify.register(autoLoad, { dir: join(process.cwd(), 'src/routes/private'), + routeParams: true }); }); @@ -67,6 +68,7 @@ fastify.register((fastify) => { fastify.register(autoLoad, { dir: join(process.cwd(), 'src/routes/public'), + routeParams: true }); const start = async () => { From 09204e17ddfca47742f0b2c59cb721c3a70bc5c6 Mon Sep 17 00:00:00 2001 From: jaxon Date: Sun, 28 Dec 2025 15:51:48 +0800 Subject: [PATCH 07/19] added endpoints to request, add and remove admin access --- backend/prisma/schema.prisma | 6 +- backend/src/prismaPlugin.ts | 5 +- backend/src/routes/private/admins/index.ts | 30 +++++- .../routes/private/admins/requests/index.ts | 98 +++++++++++++++++++ backend/src/routes/public/auth/index.ts | 6 +- backend/src/server.ts | 2 +- backend/src/shared.ts | 4 +- backend/src/types.ts | 2 +- 8 files changed, 138 insertions(+), 15 deletions(-) create mode 100644 backend/src/routes/private/admins/requests/index.ts diff --git a/backend/prisma/schema.prisma b/backend/prisma/schema.prisma index 929f3ea..a32bc62 100644 --- a/backend/prisma/schema.prisma +++ b/backend/prisma/schema.prisma @@ -9,7 +9,7 @@ datasource db { model Admin { id Int @id @default(autoincrement()) - telegram_id BigInt @unique + telegram_id String @unique } model QueueConfig { @@ -19,12 +19,12 @@ model QueueConfig { } model Queue { - telegram_id BigInt @unique + telegram_id String @unique timeCreated DateTime } model AdminRequester { id Int @id @default(autoincrement()) - telegram_id BigInt @unique + telegram_id String @unique telegram_username String } diff --git a/backend/src/prismaPlugin.ts b/backend/src/prismaPlugin.ts index e5ee789..e794619 100644 --- a/backend/src/prismaPlugin.ts +++ b/backend/src/prismaPlugin.ts @@ -22,10 +22,11 @@ const prismaPlugin: FastifyPluginAsync = fp(async (fastify, options) => { // make prisma client accessible through fastify instance fastify.decorate('prisma', prisma) - fastify.addHook('onClose', async (fastify) => { await fastify.prisma.$disconnect() - }) + }); + + }) export default prismaPlugin \ No newline at end of file diff --git a/backend/src/routes/private/admins/index.ts b/backend/src/routes/private/admins/index.ts index bd3474e..8fb9f64 100644 --- a/backend/src/routes/private/admins/index.ts +++ b/backend/src/routes/private/admins/index.ts @@ -7,22 +7,44 @@ const route: FastifyPluginAsyncZod = async (fastify, _) => { fastify.get('/', {preHandler: isAdmin}, async (request, reply) => { await fastify.prisma.admin.findMany().then((admins) => { return reply.code(200).send(admins); - }).catch((err) => { - return reply.code(500).send({error: err.message}); + }).catch((e) => { + reply.code(500); + throw new Error(e.message); }); }); - fastify.post('/:targetId', { + // remove admin endpoint + fastify.delete('/:targetId', { schema: { params: z.object({ - targetId: z.coerce.number() + targetId: z.string() }), }, preHandler: isAdmin}, async (request, reply) => { const {targetId} = request.params; + await fastify.prisma.admin.findUnique({ where: {telegram_id: targetId} }).then((admin) => { + if (admin == null) { + reply.code(404); + throw new Error("Admin not found"); + } + }).catch((e) => { + reply.code(500); + throw new Error(e.message); + }); + + + await fastify.prisma.admin.delete({ + where: { + telegram_id: targetId, + } + }).catch((e) => { + reply.code(500); + throw new Error(e.message); + }); + return reply.code(200).send({message: "Admin removed"}); }); diff --git a/backend/src/routes/private/admins/requests/index.ts b/backend/src/routes/private/admins/requests/index.ts new file mode 100644 index 0000000..8535fb1 --- /dev/null +++ b/backend/src/routes/private/admins/requests/index.ts @@ -0,0 +1,98 @@ +import {isAdmin} from "../../../../shared.js"; +import type {FastifyPluginAsyncZod} from "fastify-type-provider-zod"; +import {z} from 'zod'; + +const route: FastifyPluginAsyncZod = async (fastify, _) => { + + fastify.get('/', {preHandler: isAdmin}, async (request, reply) => { + await fastify.prisma.adminRequester.findMany().then((requesters) => { + return reply.code(200).send(requesters); + }).catch((e) => { + reply.code(500); + throw new Error(e.message); + }); + }); + + // create new admin request + fastify.post('/:targetId', { + schema: { + params: z.object({ + targetId: z.string() + }), + querystring: z.object({ + username: z.string() + }), + },}, async (request, reply) => { + + const {targetId} = request.params; + const {username} = request.query; + + await fastify.prisma.adminRequester.create({data: {telegram_id: targetId, telegram_username: username}}).then( + (requester) => { + return reply.code(200).send(requester); + } + ).catch((e) => { + if (e.code == "P2002") { + reply.code(400); + throw new Error("Request for user already exists"); + } + reply.code(500); + throw new Error(e.message); + }); + + }); + + // accepts or reject an admin request + fastify.patch('/:targetId', { + schema: { + params: z.object({ + targetId: z.string() + }), + querystring: z.object({ + accepts: z.stringbool() + }) + }, + preHandler: isAdmin}, async (request, reply) => { + + const {targetId} = request.params; + const {accepts} = request.query; + + await fastify.prisma.adminRequester.findUnique({ where: {telegram_id: targetId} }).then((requester) => { + if (requester == null) { + reply.code(404); + throw new Error("Requester not found"); + } + }).catch((e) => { + reply.code(500); + throw new Error(e.message); + }); + + await fastify.prisma.$transaction(async (tx) => { + await fastify.prisma.adminRequester.delete({where: {telegram_id: targetId}}); + if (accepts) { + await fastify.prisma.admin.create({ + data: { + telegram_id: targetId, + } + }).catch((e) => { + if (e.code == "P2002") { + reply.code(400); + throw new Error("User is already admin"); + } + }); + return reply.code(200).send({message: "Admin added successfully"}); + } else { + return reply.code(200).send({message: "Admin request denied"}); + } + }).catch((e) => { + reply.code(500); + throw new Error(e.message); + }); + + }); + + + +}; + +export default route; \ No newline at end of file diff --git a/backend/src/routes/public/auth/index.ts b/backend/src/routes/public/auth/index.ts index c4de0f4..893598a 100644 --- a/backend/src/routes/public/auth/index.ts +++ b/backend/src/routes/public/auth/index.ts @@ -7,7 +7,7 @@ import type {TelegramWebAppData, TelegramWebUser} from "../../../types.js"; const BOT_TOKEN = process.env.BOT_TOKEN!; const JWT_SECRET = process.env.JWT_SECRET!; -async function newJWT(userId: number) { +async function newJWT(userId: string) { const secret = new TextEncoder().encode( JWT_SECRET @@ -71,7 +71,7 @@ const route: FastifyPluginAsync = async (fastify, _) => { // Generate a valid JWT for development purpose if (process.env.NODE_ENV === 'development') { - return reply.code(200).send({token: await newJWT(2202843044)}); + return reply.code(200).send({token: await newJWT("2202843044")}); } const [authType, authData = ''] = (request.headers.authorization || '').split(' '); @@ -81,7 +81,7 @@ const route: FastifyPluginAsync = async (fastify, _) => { try { const appData = parseAuthData(authData); validate(new URLSearchParams(authData), appData, BOT_TOKEN); - return reply.code(200).send({token: await newJWT(appData.user.id)}); + return reply.code(200).send({token: await newJWT(appData.user.id.toString())}); } catch (e) { console.error(e); return reply.code(500).send({"error": e}); diff --git a/backend/src/server.ts b/backend/src/server.ts index 4798327..ff9c231 100644 --- a/backend/src/server.ts +++ b/backend/src/server.ts @@ -43,7 +43,7 @@ const authHook = async (request: FastifyRequest, reply: FastifyReply) => { } const { payload } = await jose.jwtVerify(request.headers.authorization!, secret); - request.userId = payload.userId as number; + request.userId = payload.userId as string; } catch (error) { return reply.code(401).send({"error": "Unauthorized"}) diff --git a/backend/src/shared.ts b/backend/src/shared.ts index b8313ea..a22552b 100644 --- a/backend/src/shared.ts +++ b/backend/src/shared.ts @@ -6,9 +6,11 @@ export async function isAdmin(request: FastifyRequest, reply: FastifyReply) { return reply.code(401).send({"error": "Missing user id"}); } - await request.server.prisma.organiser.findFirst({where: {telegram_id: request.userId!}}).then((user) => { + await request.server.prisma.admin.findFirst({where: {telegram_id: request.userId}}).then((user) => { if (user == null) { return reply.code(403).send({"error": "Endpoint requires administrator privileges"}); } + }).catch((e) => { + return reply.code(500).send({"error": e.message}); }); } \ No newline at end of file diff --git a/backend/src/types.ts b/backend/src/types.ts index 006fc86..2ec4355 100644 --- a/backend/src/types.ts +++ b/backend/src/types.ts @@ -2,7 +2,7 @@ import 'fastify'; declare module 'fastify' { interface FastifyRequest { - userId: number | undefined; + userId: string | undefined; } } From 2ba86c057bca745adf1e8abcf18373bfb974d927 Mon Sep 17 00:00:00 2001 From: jaxon Date: Sun, 28 Dec 2025 16:12:23 +0800 Subject: [PATCH 08/19] refactored error handling --- backend/src/prismaPlugin.ts | 2 +- .../routes/private/admins/requests/index.ts | 4 ++-- backend/src/routes/private/queue/status.ts | 15 +++++++----- backend/src/routes/public/auth/index.ts | 9 +++---- backend/src/server.ts | 24 +++++++++---------- backend/src/shared.ts | 9 ++++--- 6 files changed, 34 insertions(+), 29 deletions(-) diff --git a/backend/src/prismaPlugin.ts b/backend/src/prismaPlugin.ts index e794619..d4ea00e 100644 --- a/backend/src/prismaPlugin.ts +++ b/backend/src/prismaPlugin.ts @@ -9,7 +9,7 @@ declare module 'fastify' { } } -const prismaPlugin: FastifyPluginAsync = fp(async (fastify, options) => { +const prismaPlugin: FastifyPluginAsync = fp(async (fastify, _) => { const adapter = new PrismaPg({ connectionString: process.env.DATABASE_URL }) diff --git a/backend/src/routes/private/admins/requests/index.ts b/backend/src/routes/private/admins/requests/index.ts index 8535fb1..ca643bc 100644 --- a/backend/src/routes/private/admins/requests/index.ts +++ b/backend/src/routes/private/admins/requests/index.ts @@ -68,9 +68,9 @@ const route: FastifyPluginAsyncZod = async (fastify, _) => { }); await fastify.prisma.$transaction(async (tx) => { - await fastify.prisma.adminRequester.delete({where: {telegram_id: targetId}}); + await tx.adminRequester.delete({where: {telegram_id: targetId}}); if (accepts) { - await fastify.prisma.admin.create({ + await tx.admin.create({ data: { telegram_id: targetId, } diff --git a/backend/src/routes/private/queue/status.ts b/backend/src/routes/private/queue/status.ts index cc63139..d4fad28 100644 --- a/backend/src/routes/private/queue/status.ts +++ b/backend/src/routes/private/queue/status.ts @@ -8,8 +8,9 @@ const route: FastifyPluginAsyncZod = async (fastify, _) => { // get queue status fastify.get('/status', async (request, reply) => { await fastify.prisma.queueConfig.findFirst().then((queueConfig) => { - if (queueConfig === null) { - return reply.code(500).send({error: 'No queue configured'}); + if (queueConfig == null) { + reply.code(500); + throw new Error("No queue configured"); } return reply.code(200).send({status: queueConfig.isOpen}); }) @@ -21,13 +22,14 @@ const route: FastifyPluginAsyncZod = async (fastify, _) => { preHandler: isAdmin, schema: { querystring: z.object({ - open: z.coerce.boolean(), + open: z.stringbool(), })} }, async (request, reply) => { let config = await fastify.prisma.queueConfig.findFirst().then((queueConfig) => queueConfig); if (config === null) { - return reply.code(500).send({error: 'No queue configured'}); + reply.code(500); + throw new Error("No queue configured"); } config.isOpen = request.query.open; @@ -39,8 +41,9 @@ const route: FastifyPluginAsyncZod = async (fastify, _) => { data: config }).then((config) => { return reply.code(200).send(config); - }).catch((err) => { - return reply.code(500).send({error: err.message}); + }).catch((e) => { + reply.code(500); + throw new Error(e.message); }); }); diff --git a/backend/src/routes/public/auth/index.ts b/backend/src/routes/public/auth/index.ts index 893598a..2646ed8 100644 --- a/backend/src/routes/public/auth/index.ts +++ b/backend/src/routes/public/auth/index.ts @@ -82,12 +82,13 @@ const route: FastifyPluginAsync = async (fastify, _) => { const appData = parseAuthData(authData); validate(new URLSearchParams(authData), appData, BOT_TOKEN); return reply.code(200).send({token: await newJWT(appData.user.id.toString())}); - } catch (e) { - console.error(e); - return reply.code(500).send({"error": e}); + } catch (e: any) { + reply.code(500); + throw new Error(e.message); } default: - return reply.code(401).send({"error": "Unauthorized"}); + reply.code(401); + throw new Error("Unauthorized"); } diff --git a/backend/src/server.ts b/backend/src/server.ts index ff9c231..ffa1469 100644 --- a/backend/src/server.ts +++ b/backend/src/server.ts @@ -33,21 +33,19 @@ const authHook = async (request: FastifyRequest, reply: FastifyReply) => { JWT_SECRET ); - try { - - // bypass auth check during development - if (request.headers.authorization == undefined) { - if (process.env.NODE_ENV !== 'development') { - return reply.code(400).send({"error": "Missing JWT token"}); - } + // bypass auth check during development + if (request.headers.authorization == undefined) { + if (process.env.NODE_ENV !== 'development') { + reply.code(400); + throw new Error("Missing JWT"); } - const { payload } = await jose.jwtVerify(request.headers.authorization!, secret); - - request.userId = payload.userId as string; - - } catch (error) { - return reply.code(401).send({"error": "Unauthorized"}) } + const { payload } = await jose.jwtVerify(request.headers.authorization!, secret).catch((_) => { + reply.code(401); + throw new Error("Unauthorized"); + }); + + request.userId = payload.userId as string; }; diff --git a/backend/src/shared.ts b/backend/src/shared.ts index a22552b..8240222 100644 --- a/backend/src/shared.ts +++ b/backend/src/shared.ts @@ -3,14 +3,17 @@ import {type FastifyReply, type FastifyRequest} from "fastify"; export async function isAdmin(request: FastifyRequest, reply: FastifyReply) { if (request.userId == null) { - return reply.code(401).send({"error": "Missing user id"}); + reply.code(401); + throw new Error("Missing user id"); } await request.server.prisma.admin.findFirst({where: {telegram_id: request.userId}}).then((user) => { if (user == null) { - return reply.code(403).send({"error": "Endpoint requires administrator privileges"}); + reply.code(403); + throw new Error("Endpoint requires administrator privileges"); } }).catch((e) => { - return reply.code(500).send({"error": e.message}); + reply.code(500); + throw new Error(e.message); }); } \ No newline at end of file From 0f57e10954b307074fd06f372a02b5d6a0fc5916 Mon Sep 17 00:00:00 2001 From: jaxon Date: Sun, 28 Dec 2025 16:50:53 +0800 Subject: [PATCH 09/19] added endpoints for updating and getting queue configuration --- backend/src/routes/private/queue/config.ts | 53 ++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 backend/src/routes/private/queue/config.ts diff --git a/backend/src/routes/private/queue/config.ts b/backend/src/routes/private/queue/config.ts new file mode 100644 index 0000000..d3dd6aa --- /dev/null +++ b/backend/src/routes/private/queue/config.ts @@ -0,0 +1,53 @@ +import {z} from 'zod'; +import type {FastifyPluginAsyncZod} from "fastify-type-provider-zod"; +import {isAdmin} from "../../../shared.js"; + + +const route: FastifyPluginAsyncZod = async (fastify, _) => { + + // get config parameters + fastify.get('/config', {preHandler: isAdmin}, async (request, reply) => { + await fastify.prisma.queueConfig.findFirst().then((queueConfig) => { + if (queueConfig == null) { + reply.code(500); + throw new Error("No queue configured"); + } + return reply.code(200).send(queueConfig); + }) + + }); + + // update config parameters + fastify.patch('/config', { + preHandler: isAdmin, + schema: { + querystring: z.object({ + positionBeforePing: z.coerce.number() + })} + }, async (request, reply) => { + let config = await fastify.prisma.queueConfig.findFirst().then((queueConfig) => queueConfig); + + if (config === null) { + reply.code(500); + throw new Error("No queue configured"); + } + + config.positionBeforePing = request.query.positionBeforePing; + + await fastify.prisma.queueConfig.update({ + where: { + id: config.id + }, + data: config + }).then((config) => { + return reply.code(200).send(config); + }).catch((e) => { + reply.code(500); + throw new Error(e.message); + }); + + }); + +}; + +export default route; \ No newline at end of file From 7cacac6bfe1633f1ce8044c9d84b3573154f4be7 Mon Sep 17 00:00:00 2001 From: jaxon Date: Sun, 28 Dec 2025 16:51:04 +0800 Subject: [PATCH 10/19] updated endpoint documentations --- backend/README.md | 106 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 102 insertions(+), 4 deletions(-) diff --git a/backend/README.md b/backend/README.md index aadca5b..041ef42 100644 --- a/backend/README.md +++ b/backend/README.md @@ -39,12 +39,12 @@ JWT check is toggled based on directory | `src/routes/public` | **Unprotected** | Open endpoints like `/auth`. | | `src/routes/private` | **Authenticated** | Requires a valid JWT to access. | -## Endpoints Documentation +## 🚀 Endpoint Documentation ### `/queue/status` To check if queue is opened or closed. - **Method:** `GET` -- **Auth:** Require +- **Auth:** Required - **Success (200 OK):** `{ "status": boolean }` - **Possible Errors:** @@ -55,7 +55,7 @@ To check if queue is opened or closed. ### `/queue/status` Opens or closes the queue. - **Method:** `PATCH` -- **Auth:** Require (Admin) +- **Auth:** Required (Admin) - **Query Params:** `open=` - **Success (200 OK):** Returns the full updated `QueueConfig` object. - **Possible Errors:** @@ -64,4 +64,102 @@ Opens or closes the queue. |:-----|:--------------|:--------------------------------------------| | 400 | | invalid query parameters | | 403 | `Forbidden` | User is not an admin. | - | 500 | | Failed to write the update to the database. | \ No newline at end of file + | 500 | | Failed to write the update to the database. | + +### `/queue/config` +Retrieves the current queue configuration parameters. +- **Method:** `GET` +- **Auth:** Required (Admin) +- **Success (200 OK):** Returns the full `QueueConfig` object. +- **Possible Errors:** + +| Code | Error Message | Reason | +|:-----|:----------------------|:------------------------------------------------| +| 403 | `Forbidden` | User is not an admin. | +| 500 | `No queue configured` | No configuration record exists in the database. | + +--- + +### `/queue/config` +Updates the configuration parameters for the queue. +- **Method:** `PATCH` +- **Auth:** Required (Admin) +- **Query Params:** - `positionBeforePing=`: The position threshold for sending a notification. +- **Success (200 OK):** Returns the full updated `QueueConfig` object. +- **Possible Errors:** + +| Code | Error Message | Reason | +|:-----|:----------------------|:------------------------------------------| +| 400 | | Invalid query parameters. | +| 403 | `Forbidden` | User is not an admin. | +| 500 | `No queue configured` | No configuration record exists to update. | + +### `/admins` +Retrieves a list of all current users with administrative privileges. +- **Method:** `GET` +- **Auth:** Required (Admin) +- **Success (200 OK):** Returns an array of `Admin` objects. +- **Possible Errors:** + +| Code | Error Message | Reason | +|:-----|:--------------|:-------------------------------------------------------| +| 403 | `Forbidden` | User is not an admin. | + +--- + +### `/admins/:targetId` +Revokes administrative privileges from a specific user and removes them from the admin table. +- **Method:** `DELETE` +- **Auth:** Required (Admin) +- **Path Params:** - `targetId=`: The Telegram ID of the admin to be removed. +- **Success (200 OK):** Returns a confirmation message. +- **Possible Errors:** + +| Code | Error Message | Reason | +|:-----|:------------------|:---------------------------------------------------| +| 403 | `Forbidden` | User is not an admin. | +| 404 | `Admin not found` | No admin user exists with the provided `targetId`. | + +### `/admins/requests` +Retrieves a list of all current pending admin requests. +- **Method:** `GET` +- **Auth:** Required (Admin) +- **Success (200 OK):** Returns an array of `AdminRequester` objects. +- **Possible Errors:** + +| Code | Error Message | Reason | +|:-----|:--------------|:----------------------| +| 403 | `Forbidden` | User is not an admin. | + + +--- + +### `/admins/requests/:targetId` +Submits a new request for a user to become an admin. +- **Method:** `POST` +- **Auth:** Required +- **Path Params:** - `targetId=`: The Telegram ID of the user requesting admin status. +- **Query Params:** - `username=`: The Telegram username of the requester. +- **Success (200 OK):** Returns the created `AdminRequester` object. +- **Possible Errors:** + +| Code | Error Message | Reason | +|:-----|:----------------------------------|:---------------------------------------------------------------| +| 400 | `Request for user already exists` | A request with this `targetId` already exists in the database. | + +--- + +### `/admins/requests/:targetId` +Accepts or rejects a pending admin request. If accepted, the user is moved from the requesters list to the admins list. +- **Method:** `PATCH` +- **Auth:** Required (Admin) +- **Path Params:** - `targetId=`: The Telegram ID of the requester to process. +- **Query Params:** - `accepts=`: Whether to grant admin status (`true`) or deny it (`false`). +- **Success (200 OK):** Returns a confirmation message. +- **Possible Errors:** + +| Code | Error Message | Reason | +|:-----|:------------------------|:----------------------------------------------------------------------| +| 403 | `Forbidden` | User is not an admin. | +| 404 | `Requester not found` | No pending request exists for the provided `targetId`. | +| 400 | `User is already admin` | The request was accepted, but the user is already in the admin table. | From 6c84e02902202fa71bc1b0de036b62fcfc06e8e1 Mon Sep 17 00:00:00 2001 From: jaxon Date: Wed, 14 Jan 2026 17:58:42 +0800 Subject: [PATCH 11/19] updated configs --- backend/README.md | 6 +++--- backend/package.json | 2 +- backend/src/server.ts | 4 ++-- backend/tsconfig.json | 5 ----- 4 files changed, 6 insertions(+), 11 deletions(-) diff --git a/backend/README.md b/backend/README.md index 041ef42..00aa26a 100644 --- a/backend/README.md +++ b/backend/README.md @@ -9,9 +9,9 @@ Follow these steps to get your local development environment up and running. ### 1. Environment Configuration Create a `.env` file in the root directory and add the development credentials: -`BOT_TOKEN=your_development_bot_token_here` -`JWT_SECRET=your_jwt_secret_here` -`DATABASE_URL=your_database_url_here` +`BOT_TOKEN="your_development_bot_token_here"` +`JWT_SECRET="your_jwt_secret_here"` +`DATABASE_URL="your_database_url_here"` ### 2. Install Dependencies Install the required packages using npm: diff --git a/backend/package.json b/backend/package.json index 8030dcc..8c5b261 100644 --- a/backend/package.json +++ b/backend/package.json @@ -2,7 +2,7 @@ "name": "backend", "version": "1.0.0", "description": "", - "main": "src/server.ts", + "main": "dist/server.js", "scripts": { "dev": "tsx watch --env-file=.env src/server.ts", "build": "tsc", diff --git a/backend/src/server.ts b/backend/src/server.ts index ffa1469..a49cf4d 100644 --- a/backend/src/server.ts +++ b/backend/src/server.ts @@ -21,7 +21,7 @@ fastify.setValidatorCompiler(validatorCompiler); fastify.setSerializerCompiler(serializerCompiler); await fastify.register(cors, { - origin: true, + origin: true, // set this to frontend url for production methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'], allowedHeaders: ['Content-Type', 'Authorization', 'User-Id'], credentials: true, @@ -71,7 +71,7 @@ fastify.register(autoLoad, { const start = async () => { try { - await fastify.listen({ port: 3000 }) + await fastify.listen({ port: 3000, host: '0.0.0.0' }); } catch (err) { fastify.log.error(err) process.exit(1) diff --git a/backend/tsconfig.json b/backend/tsconfig.json index 6983f40..0b125bb 100644 --- a/backend/tsconfig.json +++ b/backend/tsconfig.json @@ -11,11 +11,6 @@ "target": "esnext", "esModuleInterop": true, "moduleResolution": "NodeNext", - "types": [], - // For nodejs: - // "lib": ["esnext"], - // "types": ["node"], - // and npm install -D @types/node // Other Outputs "sourceMap": true, From f5f37239d708c07da2236b6b5c012dbc031a98fb Mon Sep 17 00:00:00 2001 From: jaxon Date: Wed, 14 Jan 2026 20:18:22 +0800 Subject: [PATCH 12/19] added validation checks to ensure Telegram auth string was generated no more than 12 hrs ago --- backend/src/routes/public/auth/index.ts | 10 ++++++++++ backend/src/routes/public/index.ts | 1 + 2 files changed, 11 insertions(+) diff --git a/backend/src/routes/public/auth/index.ts b/backend/src/routes/public/auth/index.ts index 2646ed8..f03a2a6 100644 --- a/backend/src/routes/public/auth/index.ts +++ b/backend/src/routes/public/auth/index.ts @@ -22,6 +22,11 @@ async function newJWT(userId: string) { function parseAuthData(raw: string) : TelegramWebAppData { const params = new URLSearchParams(raw); + + if (params.get('chat_instance') == undefined) { + throw new Error("Invalid Telegram auth string") + } + return { auth_date: params.get('auth_date') ?? '', hash: params.get('hash') ?? '', @@ -38,6 +43,11 @@ function validate(raw: URLSearchParams, appData: TelegramWebAppData, token: stri if (!token) throw new Error("Missing token"); + // ensure the auth string was generated in the past 12 hrs + if (((Date.now() - Date.parse(appData.auth_date)) / (3600 * 1000)) > 12) { + throw new Error("Expired Telegram auth string"); + } + const dataToCheck: string[] = []; for (const [key, value] of raw.entries()) { diff --git a/backend/src/routes/public/index.ts b/backend/src/routes/public/index.ts index 46960bf..5e799d6 100644 --- a/backend/src/routes/public/index.ts +++ b/backend/src/routes/public/index.ts @@ -7,6 +7,7 @@ const route: FastifyPluginAsync = async (fastify, _) => { fastify.get('/', async (request, reply) => { // get number of admins fastify.prisma.admin.count().then((count) => console.log(count)); + }); }; From fff067296a03f7c2695cda9d11f7f13119d46ba2 Mon Sep 17 00:00:00 2001 From: Ke Sarina Yixuan Date: Thu, 15 Jan 2026 09:53:09 +0800 Subject: [PATCH 13/19] Implemented backend API endpoints --- backend/package-lock.json | 39 +++++---- .../src/routes/private/queue/entries/index.ts | 81 +++++++++++++++++++ .../routes/private/queue/entries/me/index.ts | 67 +++++++++++++++ backend/src/routes/private/queue/status.ts | 12 ++- 4 files changed, 176 insertions(+), 23 deletions(-) create mode 100644 backend/src/routes/private/queue/entries/index.ts create mode 100644 backend/src/routes/private/queue/entries/me/index.ts diff --git a/backend/package-lock.json b/backend/package-lock.json index 751359d..6d3f0a5 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -68,8 +68,7 @@ "resolved": "https://registry.npmjs.org/@electric-sql/pglite/-/pglite-0.3.2.tgz", "integrity": "sha512-zfWWa+V2ViDCY/cmUfRqeWY1yLto+EpxjXnZzenB1TyxsTiXaTWeZFIZw6mac52BsuQm0RjCnisjBtdBaXOI6w==", "devOptional": true, - "license": "Apache-2.0", - "peer": true + "license": "Apache-2.0" }, "node_modules/@electric-sql/pglite-socket": { "version": "0.0.6", @@ -1153,13 +1152,15 @@ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", "devOptional": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/debug": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "license": "MIT", + "peer": true, "dependencies": { "ms": "^2.1.3" }, @@ -1397,7 +1398,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "@fastify/ajv-compiler": "^4.0.0", "@fastify/error": "^4.0.0", @@ -1448,18 +1448,18 @@ } }, "node_modules/fastq": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", - "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", "license": "ISC", "dependencies": { "reusify": "^1.0.4" } }, "node_modules/find-my-way": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/find-my-way/-/find-my-way-9.3.0.tgz", - "integrity": "sha512-eRoFWQw+Yv2tuYlK2pjFS2jGXSxSppAs3hSQjfxVKxM5amECzIgYYc1FEI8ZmhSh/Ig+FrKEz43NLRKJjYCZVg==", + "version": "9.4.0", + "resolved": "https://registry.npmjs.org/find-my-way/-/find-my-way-9.4.0.tgz", + "integrity": "sha512-5Ye4vHsypZRYtS01ob/iwHzGRUDELlsoCftI/OZFhcLs1M0tkGPcXldE80TAZC5yYuJMBPJQQ43UHlqbJWiX2w==", "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.3", @@ -1570,7 +1570,6 @@ "integrity": "sha512-BIdolzGpDO9MQ4nu3AUuDwHZZ+KViNm+EZ75Ae55eMXMqLVhDFqEMXxtUe9Qh8hjL+pIna/frs2j6Y2yD5Ua/g==", "devOptional": true, "license": "MIT", - "peer": true, "engines": { "node": ">=16.9.0" } @@ -1665,6 +1664,7 @@ "resolved": "https://registry.npmjs.org/json-schema-resolver/-/json-schema-resolver-3.0.0.tgz", "integrity": "sha512-HqMnbz0tz2DaEJ3ntsqtx3ezzZyDE7G56A/pPY/NGmrPu76UzsWquOpHFRAf5beTNXoH2LU5cQePVvRli1nchA==", "license": "MIT", + "peer": true, "dependencies": { "debug": "^4.1.1", "fast-uri": "^3.0.5", @@ -1764,7 +1764,8 @@ "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/mysql2": { "version": "3.15.3", @@ -1879,7 +1880,6 @@ "resolved": "https://registry.npmjs.org/pg/-/pg-8.16.3.tgz", "integrity": "sha512-enxc1h0jA/aq5oSDMvqyW3q89ra6XIIDZgCX9vkMrnz5DFTw/Ny3Li2lFQ+pt3L6MCgm/5o2o8HW9hiJji+xvw==", "license": "MIT", - "peer": true, "dependencies": { "pg-connection-string": "^2.9.1", "pg-pool": "^3.10.1", @@ -2082,7 +2082,6 @@ "devOptional": true, "hasInstallScript": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "@prisma/config": "7.2.0", "@prisma/dev": "0.17.0", @@ -2338,7 +2337,8 @@ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", "devOptional": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/secure-json-parse": { "version": "4.1.0", @@ -2518,7 +2518,6 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "devOptional": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -2579,6 +2578,7 @@ "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.2.tgz", "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==", "license": "ISC", + "peer": true, "bin": { "yaml": "bin.mjs" }, @@ -2600,11 +2600,10 @@ } }, "node_modules/zod": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/zod/-/zod-4.2.1.tgz", - "integrity": "sha512-0wZ1IRqGGhMP76gLqz8EyfBXKk0J2qo2+H3fi4mcUP/KtTocoX08nmIAHl1Z2kJIZbZee8KOpBCSNPRgauucjw==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.4.tgz", + "integrity": "sha512-Zw/uYiiyF6pUT1qmKbZziChgNPRu+ZRneAsMUDU6IwmXdWt5JwcUfy2bvLOCUtz5UniaN/Zx5aFttZYbYc7O/A==", "license": "MIT", - "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } diff --git a/backend/src/routes/private/queue/entries/index.ts b/backend/src/routes/private/queue/entries/index.ts new file mode 100644 index 0000000..750fd1c --- /dev/null +++ b/backend/src/routes/private/queue/entries/index.ts @@ -0,0 +1,81 @@ +import {z} from 'zod'; +import type { FastifyPluginAsyncZod } from "fastify-type-provider-zod"; +import { isAdmin } from "../../../../shared.js"; + +const route: FastifyPluginAsyncZod = async (fastify) => { + + /** + * GET /queue/entries + * Returns the entire queue ordered from first to last (admin-only) + */ + fastify.get( + "/", + { preHandler: isAdmin }, // only admins can call this endpoint + async (_request, reply) => { + try { + const entries = await fastify.prisma.queue.findMany({ + orderBy: { timeCreated: "asc" }, + }); + + return reply.code(200).send({ entries }); + } catch (e: any) { + reply.code(500); + throw new Error("Failed to fetch queue entries"); + } + } + ); + + /** + * POST /queue/entries + * Adds the user to the queue + */ + fastify.post( + "/", + async (request, reply) => { + const userId = request.userId!; + + // 1) Queue must exist and be open + const config = await fastify.prisma.queueConfig.findFirst(); + if (!config || !config.isOpen) { + reply.code(500); + throw new Error("No queue configured"); + } + + // 2) Prevent duplicate entries + const alreadyInQueue = await fastify.prisma.queue.findUnique({ + where: { telegram_id: userId }, + }); + + if (alreadyInQueue) { + reply.code(409); + throw new Error("User already in queue"); + } + + // 3) Add user to queue + await fastify.prisma.queue.create({ + data: { + telegram_id: userId, + timeCreated: new Date(), + }, + }); + + // 4) Compute position and number of people ahead (for frontend) + const allEntries = await fastify.prisma.queue.findMany({ + orderBy: { timeCreated: "asc" }, + select: { telegram_id: true }, + }); + + const position = + allEntries.findIndex((e) => e.telegram_id === userId) + 1; + + return reply.code(201).send({ + joined: true, + position, + ahead: position - 1, + }); + } + ); + +}; + +export default route; diff --git a/backend/src/routes/private/queue/entries/me/index.ts b/backend/src/routes/private/queue/entries/me/index.ts new file mode 100644 index 0000000..b478d03 --- /dev/null +++ b/backend/src/routes/private/queue/entries/me/index.ts @@ -0,0 +1,67 @@ +import {z} from 'zod'; +import type { FastifyPluginAsyncZod } from "fastify-type-provider-zod"; +import { isAdmin } from "../../../../../shared.js"; + +const route: FastifyPluginAsyncZod = async (fastify) => { + + /** + * DELETE /queue/entries/me + * Delete the user from the queue + */ + fastify.delete("/", async (request, reply) => { + await fastify.prisma.queue + .delete({ + where: { telegram_id: request.userId! }, + }) + .then(() => { + return reply.code(200).send({ left: true }); + }) + .catch((e: any) => { + // Prisma "record not found" error code (user not in queue) + if (e?.code === "P2025") { + reply.code(400); + throw new Error("User not in queue"); + } + + reply.code(500); + throw new Error(e?.message ?? "Failed to leave queue"); + }); + }); + + /** + * GET /queue/entries/me + * Get the number of people ahead of user + */ + fastify.get("/", async (request, reply) => { + const userId = request.userId; + + // Queue must exist and be open + const config = await fastify.prisma.queueConfig.findFirst(); + if (!config || !config.isOpen) { + reply.code(500); + throw new Error("No queue configured"); + } + + // Fetch queue in order and locate the user + const entries = await fastify.prisma.queue.findMany({ + orderBy: { timeCreated: "asc" }, + select: { telegram_id: true }, + }); + + const index = entries.findIndex((e) => e.telegram_id === userId); + + // User not in queue + if (index === -1) { + reply.code(400); + throw new Error("User not in queue"); + } + + // index = number of people ahead (0 means you're first) + return reply.code(200).send({ + ahead: index, + }); + }); + +}; + +export default route; diff --git a/backend/src/routes/private/queue/status.ts b/backend/src/routes/private/queue/status.ts index d4fad28..3db198b 100644 --- a/backend/src/routes/private/queue/status.ts +++ b/backend/src/routes/private/queue/status.ts @@ -5,7 +5,10 @@ import {isAdmin} from "../../../shared.js"; const route: FastifyPluginAsyncZod = async (fastify, _) => { - // get queue status + /** + * GET /queue/status + * Checks if the queue is open + */ fastify.get('/status', async (request, reply) => { await fastify.prisma.queueConfig.findFirst().then((queueConfig) => { if (queueConfig == null) { @@ -17,7 +20,10 @@ const route: FastifyPluginAsyncZod = async (fastify, _) => { }); - // update queue status + /** + * PATCH /queue/status (admin-only) + * Opens/closes the queue + */ fastify.patch('/status', { preHandler: isAdmin, schema: { @@ -50,4 +56,4 @@ const route: FastifyPluginAsyncZod = async (fastify, _) => { }; -export default route; \ No newline at end of file +export default route; From 803775d0937c831302ef8b3010a4b3f470e03142 Mon Sep 17 00:00:00 2001 From: syk17 Date: Thu, 15 Jan 2026 10:34:26 +0800 Subject: [PATCH 14/19] Update README.md --- backend/README.md | 53 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/backend/README.md b/backend/README.md index 00aa26a..99cc81b 100644 --- a/backend/README.md +++ b/backend/README.md @@ -94,6 +94,59 @@ Updates the configuration parameters for the queue. | 403 | `Forbidden` | User is not an admin. | | 500 | `No queue configured` | No configuration record exists to update. | +### `/queue/entries` +Updates the configuration parameters for the queue. +- **Method:** `GET` +- **Auth:** Required (Admin) +- **Success (200 OK):** `{ "entries": Queue[] }` +- **Possible Errors:** + +| Code | Error Message | Reason | +| :--- | :------------------------------ | :---------------------------------------------- | +| 403 | `Forbidden` | User is not an admin. | +| 500 | `Failed to fetch queue entries` | Failed to read queue entries from the database. | + +### `/queue/entries` +Adds the current user to the queue. +- **Method:** `POST` +- **Auth:** Required +- **Success (200 OK):** `{ "joined": boolean, "position": number, "ahead": number }` +- **Possible Errors:** + +| Code | Error Message | Reason | +|:-----|:------------------------|:-----------------------------------------------------------| +| 400 | `Queue not started` | Queue is closed (not open for joining). | +| 500 | | Failed to create the queue entry in the database. | + +| Code | Error Message | Reason | +| :--- | :---------------------------- | :---------------------------------------------- | +| 409 | `User already in queue` | User is already in the queue. | +| 500 | `No queue configured` | No configuration record exists in the database. | + +### `/queue/entries/me` +Removes the current user from the queue. +- **Method:** `DELETE` +- **Auth:** Required +- **Success (200 OK):** `{ "left": true }` +- **Possible Errors:** + +| Code | Error Message | Reason | +| :--- | :---------------------- | :---------------------------------------------- | +| 400 | `User not in queue` | User is not currently in the queue. | +| 500 | `Failed to leave queue` | Failed to remove user from the queue. | + +### `/queue/entries/me` +Returns how many people are ahead of the current user in the queue. +- **Method:** `GET` +- **Auth:** Required +- **Success (200 OK):** `{ "ahead": number }` +- **Possible Errors:** + +| Code | Error Message | Reason | +| :--- | :------------------------------------ | :---------------------------------------------- | +| 400 | `User not in queue` | User is not currently in the queue. | +| 500 | `No queue configured` | No configuration record exists in the database. | + ### `/admins` Retrieves a list of all current users with administrative privileges. - **Method:** `GET` From d21ede72e5cdacc133a157fcdbdd02648b57a4dd Mon Sep 17 00:00:00 2001 From: syk17 Date: Thu, 15 Jan 2026 10:35:46 +0800 Subject: [PATCH 15/19] Update README.md --- backend/README.md | 5 ----- 1 file changed, 5 deletions(-) diff --git a/backend/README.md b/backend/README.md index 99cc81b..625463c 100644 --- a/backend/README.md +++ b/backend/README.md @@ -113,11 +113,6 @@ Adds the current user to the queue. - **Success (200 OK):** `{ "joined": boolean, "position": number, "ahead": number }` - **Possible Errors:** -| Code | Error Message | Reason | -|:-----|:------------------------|:-----------------------------------------------------------| -| 400 | `Queue not started` | Queue is closed (not open for joining). | -| 500 | | Failed to create the queue entry in the database. | - | Code | Error Message | Reason | | :--- | :---------------------------- | :---------------------------------------------- | | 409 | `User already in queue` | User is already in the queue. | From 068c8242ce47ef23d7f6cfd771c7ad8001769bb4 Mon Sep 17 00:00:00 2001 From: jaxon Date: Thu, 15 Jan 2026 16:23:19 +0800 Subject: [PATCH 16/19] cache queueConfig --- backend/src/prismaPlugin.ts | 7 - backend/src/queueConfigPlugin.ts | 28 ++++ backend/src/routes/private/queue/config.ts | 15 +- .../src/routes/private/queue/entries/index.ts | 129 +++++++++--------- backend/src/routes/private/queue/status.ts | 13 +- backend/src/server.ts | 3 + backend/src/types.ts | 10 ++ 7 files changed, 113 insertions(+), 92 deletions(-) create mode 100644 backend/src/queueConfigPlugin.ts diff --git a/backend/src/prismaPlugin.ts b/backend/src/prismaPlugin.ts index d4ea00e..25d6da8 100644 --- a/backend/src/prismaPlugin.ts +++ b/backend/src/prismaPlugin.ts @@ -3,12 +3,6 @@ import type {FastifyPluginAsync} from 'fastify' import {PrismaClient} from "./generated/prisma/client.js"; import { PrismaPg } from '@prisma/adapter-pg' -declare module 'fastify' { - interface FastifyInstance { - prisma: PrismaClient - } -} - const prismaPlugin: FastifyPluginAsync = fp(async (fastify, _) => { const adapter = new PrismaPg({ connectionString: process.env.DATABASE_URL }) @@ -26,7 +20,6 @@ const prismaPlugin: FastifyPluginAsync = fp(async (fastify, _) => { await fastify.prisma.$disconnect() }); - }) export default prismaPlugin \ No newline at end of file diff --git a/backend/src/queueConfigPlugin.ts b/backend/src/queueConfigPlugin.ts new file mode 100644 index 0000000..fc3b85e --- /dev/null +++ b/backend/src/queueConfigPlugin.ts @@ -0,0 +1,28 @@ +import fp from 'fastify-plugin' +import type {FastifyPluginAsync} from 'fastify' +import type {QueueConfigModel} from "./generated/prisma/models/QueueConfig.js"; + +const queueConfigPlugin: FastifyPluginAsync = fp(async (fastify, _) => { + + let cacheConfig: QueueConfigModel | undefined; + + // force if true forces a db fetch + let getQueueConfig: (force?: boolean) => Promise = async (force?: boolean) => { + if (cacheConfig != null && !force) { + return cacheConfig!; + } + await fastify.prisma.queueConfig.findFirst().then((queueConfig) => { + if (queueConfig == null) { + throw new Error("No queue configured"); + } else { + cacheConfig = queueConfig; + } + }); + return cacheConfig!; + } + + fastify.decorate("getQueueConfig", getQueueConfig); + +}) + +export default queueConfigPlugin \ No newline at end of file diff --git a/backend/src/routes/private/queue/config.ts b/backend/src/routes/private/queue/config.ts index d3dd6aa..7f91138 100644 --- a/backend/src/routes/private/queue/config.ts +++ b/backend/src/routes/private/queue/config.ts @@ -6,15 +6,8 @@ import {isAdmin} from "../../../shared.js"; const route: FastifyPluginAsyncZod = async (fastify, _) => { // get config parameters - fastify.get('/config', {preHandler: isAdmin}, async (request, reply) => { - await fastify.prisma.queueConfig.findFirst().then((queueConfig) => { - if (queueConfig == null) { - reply.code(500); - throw new Error("No queue configured"); - } - return reply.code(200).send(queueConfig); - }) - + fastify.get('/config', {preHandler: isAdmin}, async (_, reply) => { + return reply.code(200).send(await fastify.getQueueConfig()) }); // update config parameters @@ -39,7 +32,9 @@ const route: FastifyPluginAsyncZod = async (fastify, _) => { id: config.id }, data: config - }).then((config) => { + }).then(async (config) => { + // refresh local copy of queueConfig + await fastify.getQueueConfig(true); return reply.code(200).send(config); }).catch((e) => { reply.code(500); diff --git a/backend/src/routes/private/queue/entries/index.ts b/backend/src/routes/private/queue/entries/index.ts index 750fd1c..e7213af 100644 --- a/backend/src/routes/private/queue/entries/index.ts +++ b/backend/src/routes/private/queue/entries/index.ts @@ -1,80 +1,77 @@ -import {z} from 'zod'; -import type { FastifyPluginAsyncZod } from "fastify-type-provider-zod"; -import { isAdmin } from "../../../../shared.js"; +import type {FastifyPluginAsyncZod} from "fastify-type-provider-zod"; +import {isAdmin} from "../../../../shared.js"; const route: FastifyPluginAsyncZod = async (fastify) => { - /** - * GET /queue/entries - * Returns the entire queue ordered from first to last (admin-only) - */ - fastify.get( - "/", - { preHandler: isAdmin }, // only admins can call this endpoint - async (_request, reply) => { - try { - const entries = await fastify.prisma.queue.findMany({ - orderBy: { timeCreated: "asc" }, - }); + /** + * GET /queue/entries + * Returns the entire queue ordered from first to last (admin-only) + */ + fastify.get( + "/", + {preHandler: isAdmin}, + async (_request, reply) => { + try { + const entries = await fastify.prisma.queue.findMany({ + orderBy: {timeCreated: "asc"}, + }); + return reply.code(200).send({entries}); + } catch (e: any) { + reply.code(500); + throw new Error("Failed to fetch queue entries"); + } + } + ); - return reply.code(200).send({ entries }); - } catch (e: any) { - reply.code(500); - throw new Error("Failed to fetch queue entries"); - } - } - ); + /** + * POST /queue/entries + * Adds the user to the queue + */ + fastify.post( + "/", + async (request, reply) => { + const userId = request.userId!; - /** - * POST /queue/entries - * Adds the user to the queue - */ - fastify.post( - "/", - async (request, reply) => { - const userId = request.userId!; + // Queue must exist and be open + if (!(await fastify.getQueueConfig()).isOpen) { + reply.code(400); + throw new Error("Queue is closed"); + } - // 1) Queue must exist and be open - const config = await fastify.prisma.queueConfig.findFirst(); - if (!config || !config.isOpen) { - reply.code(500); - throw new Error("No queue configured"); - } + // Prevent duplicate entries + const alreadyInQueue = await fastify.prisma.queue.findUnique({ + where: {telegram_id: userId}, + }); - // 2) Prevent duplicate entries - const alreadyInQueue = await fastify.prisma.queue.findUnique({ - where: { telegram_id: userId }, - }); + if (alreadyInQueue) { + reply.code(409); + throw new Error("User already in queue"); + } - if (alreadyInQueue) { - reply.code(409); - throw new Error("User already in queue"); - } + // Add user to queue + await fastify.prisma.queue.create({ + data: { + telegram_id: userId, + timeCreated: new Date(), + }, + }); - // 3) Add user to queue - await fastify.prisma.queue.create({ - data: { - telegram_id: userId, - timeCreated: new Date(), - }, - }); + // Compute position and number of people ahead (for frontend) + const allEntries = await fastify.prisma.queue.findMany({ + orderBy: {timeCreated: "asc"}, + select: {telegram_id: true}, + }); - // 4) Compute position and number of people ahead (for frontend) - const allEntries = await fastify.prisma.queue.findMany({ - orderBy: { timeCreated: "asc" }, - select: { telegram_id: true }, - }); + const position = + allEntries.findIndex((e) => e.telegram_id === userId) + 1; - const position = - allEntries.findIndex((e) => e.telegram_id === userId) + 1; - - return reply.code(201).send({ - joined: true, - position, - ahead: position - 1, - }); - } - ); + return reply.code(201).send({ + joined: true, + position, + ahead: position - 1, + }); + } + ); }; diff --git a/backend/src/routes/private/queue/status.ts b/backend/src/routes/private/queue/status.ts index 3db198b..65cb4d3 100644 --- a/backend/src/routes/private/queue/status.ts +++ b/backend/src/routes/private/queue/status.ts @@ -10,14 +10,7 @@ const route: FastifyPluginAsyncZod = async (fastify, _) => { * Checks if the queue is open */ fastify.get('/status', async (request, reply) => { - await fastify.prisma.queueConfig.findFirst().then((queueConfig) => { - if (queueConfig == null) { - reply.code(500); - throw new Error("No queue configured"); - } - return reply.code(200).send({status: queueConfig.isOpen}); - }) - + return reply.code(200).send({status: (await fastify.getQueueConfig()).isOpen}); }); /** @@ -45,7 +38,9 @@ const route: FastifyPluginAsyncZod = async (fastify, _) => { id: config.id }, data: config - }).then((config) => { + }).then(async (config) => { + // update local copy of queueConfig + await fastify.getQueueConfig(true); return reply.code(200).send(config); }).catch((e) => { reply.code(500); diff --git a/backend/src/server.ts b/backend/src/server.ts index a49cf4d..98612af 100644 --- a/backend/src/server.ts +++ b/backend/src/server.ts @@ -9,6 +9,7 @@ import { serializerCompiler, type ZodTypeProvider } from 'fastify-type-provider-zod'; +import queueConfigPlugin from "./queueConfigPlugin.js"; const JWT_SECRET = process.env.JWT_SECRET!; @@ -51,6 +52,8 @@ const authHook = async (request: FastifyRequest, reply: FastifyReply) => { // Init sql db connection fastify.register(prismaPlugin); +// Register customs plugins +fastify.register(queueConfigPlugin); fastify.register((fastify) => { diff --git a/backend/src/types.ts b/backend/src/types.ts index 2ec4355..fbe5b6c 100644 --- a/backend/src/types.ts +++ b/backend/src/types.ts @@ -1,4 +1,6 @@ import 'fastify'; +import type {PrismaClient} from "./generated/prisma/client.js"; +import type {QueueConfigModel} from "./generated/prisma/models/QueueConfig.js"; declare module 'fastify' { interface FastifyRequest { @@ -6,6 +8,14 @@ declare module 'fastify' { } } +declare module 'fastify' { + interface FastifyInstance { + prisma: PrismaClient + getQueueConfig(force?: boolean): Promise + } +} + + export interface TelegramWebUser { id: number; first_name: string; From f536a0212a575e288d5c823000ad16d3a05f6a84 Mon Sep 17 00:00:00 2001 From: jaxon Date: Thu, 15 Jan 2026 17:00:50 +0800 Subject: [PATCH 17/19] added endpoint to advance queue --- backend/README.md | 16 ++++++--- backend/src/routes/private/queue/next.ts | 43 ++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 5 deletions(-) create mode 100644 backend/src/routes/private/queue/next.ts diff --git a/backend/README.md b/backend/README.md index 625463c..e8a4fb4 100644 --- a/backend/README.md +++ b/backend/README.md @@ -78,7 +78,6 @@ Retrieves the current queue configuration parameters. | 403 | `Forbidden` | User is not an admin. | | 500 | `No queue configured` | No configuration record exists in the database. | ---- ### `/queue/config` Updates the configuration parameters for the queue. @@ -94,6 +93,17 @@ Updates the configuration parameters for the queue. | 403 | `Forbidden` | User is not an admin. | | 500 | `No queue configured` | No configuration record exists to update. | +### `/queue/next` +Advances the queue by removing the person at the front and notifying the next `N` users in line via Telegram. +- **Method:** `POST` +- **Auth:** Required (Admin) +- **Success (200 OK):** Returns the updated queue or a message if the queue is empty. +- **Possible Errors:** + +| Code | Error Message | Reason | +|:-----|:--------------|:-------| +| 403 | `Forbidden` | User is not authorized as an admin. | + ### `/queue/entries` Updates the configuration parameters for the queue. - **Method:** `GET` @@ -153,7 +163,6 @@ Retrieves a list of all current users with administrative privileges. |:-----|:--------------|:-------------------------------------------------------| | 403 | `Forbidden` | User is not an admin. | ---- ### `/admins/:targetId` Revokes administrative privileges from a specific user and removes them from the admin table. @@ -180,8 +189,6 @@ Retrieves a list of all current pending admin requests. | 403 | `Forbidden` | User is not an admin. | ---- - ### `/admins/requests/:targetId` Submits a new request for a user to become an admin. - **Method:** `POST` @@ -195,7 +202,6 @@ Submits a new request for a user to become an admin. |:-----|:----------------------------------|:---------------------------------------------------------------| | 400 | `Request for user already exists` | A request with this `targetId` already exists in the database. | ---- ### `/admins/requests/:targetId` Accepts or rejects a pending admin request. If accepted, the user is moved from the requesters list to the admins list. diff --git a/backend/src/routes/private/queue/next.ts b/backend/src/routes/private/queue/next.ts new file mode 100644 index 0000000..33f69ab --- /dev/null +++ b/backend/src/routes/private/queue/next.ts @@ -0,0 +1,43 @@ +import type {FastifyPluginAsyncZod} from "fastify-type-provider-zod"; +import {isAdmin} from "../../../shared.js"; + +const BOT_TOKEN = process.env.BOT_TOKEN; + +const route: FastifyPluginAsyncZod = async (fastify, _) => { + + fastify.post('/next', {preHandler: isAdmin}, async (request, reply) => { + + const allEntries = await fastify.prisma.queue.findMany({ + orderBy: {timeCreated: "asc"}, + }); + + if (allEntries.length === 0) { + return reply.code(200).send({message: "Queue is empty"}); + } + + const top = allEntries.shift(); + // remove top user from the queue + await fastify.prisma.queue.delete({where: {telegram_id: top!.telegram_id}}); + + const message = `Your turn is coming up! Please start making your way over.` + + // pings the top n user + for (let i = 0; i < (await fastify.getQueueConfig()).positionBeforePing; i++) { + if (allEntries[i] == undefined) { + break; + } + const queryString = new URLSearchParams( + {'chat_id': allEntries[i]!.telegram_id, 'text': message, 'parse_mode': 'Markdown'}).toString(); + // hit Telegram API to send user message + fetch(`https://api.telegram.org/bot${BOT_TOKEN}/test/sendMessage?${queryString}`, { + method: 'POST', + }) + } + + return reply.code(200).send({allEntries: allEntries}); + + }); + +}; + +export default route; \ No newline at end of file From 375cd6bb11c818a965ecce083bd7a8678f3d0a0e Mon Sep 17 00:00:00 2001 From: jaxon Date: Thu, 15 Jan 2026 17:04:41 +0800 Subject: [PATCH 18/19] improved advance queue endpoint output --- backend/README.md | 32 ++++++++++++------------ backend/src/routes/private/queue/next.ts | 4 +-- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/backend/README.md b/backend/README.md index e8a4fb4..a09f230 100644 --- a/backend/README.md +++ b/backend/README.md @@ -97,11 +97,11 @@ Updates the configuration parameters for the queue. Advances the queue by removing the person at the front and notifying the next `N` users in line via Telegram. - **Method:** `POST` - **Auth:** Required (Admin) -- **Success (200 OK):** Returns the updated queue or a message if the queue is empty. +- **Success (200 OK):** Returns the updated queue or an empty list if queue is empty. - **Possible Errors:** -| Code | Error Message | Reason | -|:-----|:--------------|:-------| +| Code | Error Message | Reason | +|:-----|:--------------|:------------------------------------| | 403 | `Forbidden` | User is not authorized as an admin. | ### `/queue/entries` @@ -112,7 +112,7 @@ Updates the configuration parameters for the queue. - **Possible Errors:** | Code | Error Message | Reason | -| :--- | :------------------------------ | :---------------------------------------------- | +|:-----|:--------------------------------|:------------------------------------------------| | 403 | `Forbidden` | User is not an admin. | | 500 | `Failed to fetch queue entries` | Failed to read queue entries from the database. | @@ -123,10 +123,10 @@ Adds the current user to the queue. - **Success (200 OK):** `{ "joined": boolean, "position": number, "ahead": number }` - **Possible Errors:** -| Code | Error Message | Reason | -| :--- | :---------------------------- | :---------------------------------------------- | -| 409 | `User already in queue` | User is already in the queue. | -| 500 | `No queue configured` | No configuration record exists in the database. | +| Code | Error Message | Reason | +|:-----|:------------------------|:------------------------------------------------| +| 409 | `User already in queue` | User is already in the queue. | +| 500 | `No queue configured` | No configuration record exists in the database. | ### `/queue/entries/me` Removes the current user from the queue. @@ -135,10 +135,10 @@ Removes the current user from the queue. - **Success (200 OK):** `{ "left": true }` - **Possible Errors:** -| Code | Error Message | Reason | -| :--- | :---------------------- | :---------------------------------------------- | -| 400 | `User not in queue` | User is not currently in the queue. | -| 500 | `Failed to leave queue` | Failed to remove user from the queue. | +| Code | Error Message | Reason | +|:-----|:------------------------|:--------------------------------------| +| 400 | `User not in queue` | User is not currently in the queue. | +| 500 | `Failed to leave queue` | Failed to remove user from the queue. | ### `/queue/entries/me` Returns how many people are ahead of the current user in the queue. @@ -147,10 +147,10 @@ Returns how many people are ahead of the current user in the queue. - **Success (200 OK):** `{ "ahead": number }` - **Possible Errors:** -| Code | Error Message | Reason | -| :--- | :------------------------------------ | :---------------------------------------------- | -| 400 | `User not in queue` | User is not currently in the queue. | -| 500 | `No queue configured` | No configuration record exists in the database. | +| Code | Error Message | Reason | +|:-----|:----------------------|:------------------------------------------------| +| 400 | `User not in queue` | User is not currently in the queue. | +| 500 | `No queue configured` | No configuration record exists in the database. | ### `/admins` Retrieves a list of all current users with administrative privileges. diff --git a/backend/src/routes/private/queue/next.ts b/backend/src/routes/private/queue/next.ts index 33f69ab..672b836 100644 --- a/backend/src/routes/private/queue/next.ts +++ b/backend/src/routes/private/queue/next.ts @@ -12,7 +12,7 @@ const route: FastifyPluginAsyncZod = async (fastify, _) => { }); if (allEntries.length === 0) { - return reply.code(200).send({message: "Queue is empty"}); + return reply.code(200).send({entries: []}); } const top = allEntries.shift(); @@ -34,7 +34,7 @@ const route: FastifyPluginAsyncZod = async (fastify, _) => { }) } - return reply.code(200).send({allEntries: allEntries}); + return reply.code(200).send({entries: allEntries}); }); From 86456e65dc3c8a4558e5fb22f5d6489cb4624d07 Mon Sep 17 00:00:00 2001 From: jaxon Date: Thu, 15 Jan 2026 17:28:03 +0800 Subject: [PATCH 19/19] added documentation for auth endpoint --- backend/README.md | 20 ++++++++++++++++++++ backend/src/queueConfigPlugin.ts | 2 ++ 2 files changed, 22 insertions(+) diff --git a/backend/README.md b/backend/README.md index a09f230..8d0c439 100644 --- a/backend/README.md +++ b/backend/README.md @@ -41,6 +41,26 @@ JWT check is toggled based on directory ## 🚀 Endpoint Documentation +### `/auth` +Authenticates a user via Telegram Mini App (TMA) initialization data and issues a short-lived JWT. In development environments, it returns a mock token for testing purposes. + +- **Method:** `GET` +- **Auth:** No +- **Headers:** `Authorization`: `tma ` +- **Success (200 OK):** Returns a signed JWT. `{ "token": "jwt" }` + +- **Possible Errors:** + +| Code | Error Message | Reason | +|:-----|:-------------------------------|:------------------------------------------------------------------------------| +| 401 | `Unauthorized` | Authorization header is missing or prefix is not `tma`. | +| 500 | `Invalid Telegram auth string` | The init data is malformed or missing required fields like `chat_instance`. | +| 500 | `Expired Telegram auth string` | The `auth_date` provided is older than 12 hours. | +| 500 | `Invalid signature` | The HMAC signature verification failed against the `BOT_TOKEN`. | +| 500 | `Missing hash` | The `hash` parameter is missing from the provided authentication data. | + +> **Note:** When `NODE_ENV` is set to `development`, this endpoint returns a token for a default user ID (`2202843044`) without validating headers. + ### `/queue/status` To check if queue is opened or closed. - **Method:** `GET` diff --git a/backend/src/queueConfigPlugin.ts b/backend/src/queueConfigPlugin.ts index fc3b85e..0097bdb 100644 --- a/backend/src/queueConfigPlugin.ts +++ b/backend/src/queueConfigPlugin.ts @@ -6,6 +6,8 @@ const queueConfigPlugin: FastifyPluginAsync = fp(async (fastify, _) => { let cacheConfig: QueueConfigModel | undefined; + // QueueConfig should only be accessed through this function to reduce calls to DB + // force if true forces a db fetch let getQueueConfig: (force?: boolean) => Promise = async (force?: boolean) => { if (cacheConfig != null && !force) {