diff --git a/package-lock.json b/package-lock.json index 919f84e..3521951 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1379,9 +1379,9 @@ } }, "node_modules/@emnapi/runtime": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.1.tgz", - "integrity": "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==", + "version": "1.11.2", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.2.tgz", + "integrity": "sha512-kyOl3X0DuTiT1h2ft8r2fYO8JYtU9a9Xis/zBSiGArNaagCOWx90N1k2wxp18czFDH+OgcWGb5ZP/XMt3dcyPA==", "license": "MIT", "optional": true, "dependencies": { @@ -1512,12 +1512,12 @@ } }, "node_modules/@graphql-tools/merge": { - "version": "9.1.10", - "resolved": "https://registry.npmjs.org/@graphql-tools/merge/-/merge-9.1.10.tgz", - "integrity": "sha512-CsUCcPXv/HMWpPZ/xoOioSWFuAaAsGGPIdqQbjDv9X1sHGs0muQRW5FK7vUBN4OH/D7aPqpiHvKmRsrHz+ScuQ==", + "version": "9.1.11", + "resolved": "https://registry.npmjs.org/@graphql-tools/merge/-/merge-9.1.11.tgz", + "integrity": "sha512-APuOIPBUm9NQQCl1qfKC39zclR1p8UVsEhCg9t9nYF3kSe859F9/wq4VmBqiz/ETuABS7Bm7WA+yDYWC6HoD8A==", "license": "MIT", "dependencies": { - "@graphql-tools/utils": "^11.1.1", + "@graphql-tools/utils": "^11.2.0", "tslib": "^2.4.0" }, "engines": { @@ -1528,13 +1528,13 @@ } }, "node_modules/@graphql-tools/schema": { - "version": "10.0.34", - "resolved": "https://registry.npmjs.org/@graphql-tools/schema/-/schema-10.0.34.tgz", - "integrity": "sha512-wGCiUuwqDtlXtDvQ00CI+4u0pd3yd/xGGYOiwc9L63KJZwSXdrUv8ajpJ5lMalawc0mpjcCB0ljEQSeUhajuag==", + "version": "10.0.35", + "resolved": "https://registry.npmjs.org/@graphql-tools/schema/-/schema-10.0.35.tgz", + "integrity": "sha512-g/7IUmuLr3y4xoK1PDUm3B7arLBEMZatcbCIMf2690CU66xncY+GlMH37G0Sbm2lJY1/ELFpXa/S50rHKmoEkQ==", "license": "MIT", "dependencies": { - "@graphql-tools/merge": "^9.1.10", - "@graphql-tools/utils": "^11.1.1", + "@graphql-tools/merge": "^9.1.11", + "@graphql-tools/utils": "^11.2.0", "tslib": "^2.4.0" }, "engines": { @@ -1545,9 +1545,9 @@ } }, "node_modules/@graphql-tools/utils": { - "version": "11.1.1", - "resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-11.1.1.tgz", - "integrity": "sha512-MuWwacINZZV6mX1ZSk6CcV4XVQLsbKBG/hLd0QPhe4GHxjqr2ATjKsnnwlB0TKI+QQvj2U8ewu8WeAzz9kC2Xg==", + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-11.2.0.tgz", + "integrity": "sha512-eu9h1R3j/wWc4rvmYJF5AKtlwniDzstrZ/c6KSz+HdI+n7I7iog9xyKmBfpUwSbG1TqPNZBzWjFMkzdYOKq6Bg==", "license": "MIT", "dependencies": { "@graphql-typed-document-node/core": "^3.1.1", @@ -4374,9 +4374,9 @@ "license": "BSD-3-Clause" }, "node_modules/@protobufjs/utf8": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.1.tgz", - "integrity": "sha512-oOAWABowe8EAbMyWKM0tYDKi8Yaox52D+HWZhAIJqQXbqe0xI/GV7FhLWqlEKreMkfDjshR5FKgi3mnle0h6Eg==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.2.tgz", + "integrity": "sha512-b1UQwcEZ4yCnMCD8DAL1VlbvBJE9/IX4FTIp7BG1xYpf29SLazLSrqUkj4w7Y5y7cCVP6E5tcqqcI0xemPkHug==", "license": "BSD-3-Clause" }, "node_modules/@redis/bloom": { @@ -5582,14 +5582,14 @@ "license": "Apache-2.0" }, "node_modules/@zone-eu/mailsplit": { - "version": "5.4.13", - "resolved": "https://registry.npmjs.org/@zone-eu/mailsplit/-/mailsplit-5.4.13.tgz", - "integrity": "sha512-j40NeNlSAivqnKEjzZYsGP/bKWr2zwuyb8XOYsC3i0ZlfM5UnTYTa7aCRkE8rYQvVzE1dRjVYdvZ1Epi6x+h6w==", + "version": "5.4.14", + "resolved": "https://registry.npmjs.org/@zone-eu/mailsplit/-/mailsplit-5.4.14.tgz", + "integrity": "sha512-rz0FQOhN3Vq1XrSeSSa9+dPcaFbBxmQPjiZm6zS9oxdVHV7rOWIAYX3yP2YAUf0qBncY8CI+NogzPCmMVrMXcw==", "license": "(MIT OR EUPL-1.1+)", "optional": true, "dependencies": { "libbase64": "1.3.0", - "libmime": "5.4.0", + "libmime": "5.4.1", "libqp": "2.1.1" } }, @@ -6250,9 +6250,9 @@ } }, "node_modules/bare-fs": { - "version": "4.7.2", - "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.7.2.tgz", - "integrity": "sha512-aTvMFUWkBmjzKtEQMDGGDNF8bkfpD5N1b/FCwt7A3wrU4t1o/e/85Wzkluh6JlODCjqVESYCkQCdTXqZ9G7VFg==", + "version": "4.7.3", + "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.7.3.tgz", + "integrity": "sha512-xRgplks8SvcKkdlv2M6Z2LZmRsmqd+x0nXXGXeMEjwdibj1HSDrlnqBRLeYdMvsgCox7Bq0e+DHwfczOfsn6IA==", "license": "Apache-2.0", "dependencies": { "bare-events": "^2.5.4", @@ -6366,9 +6366,9 @@ } }, "node_modules/baseline-browser-mapping": { - "version": "2.10.40", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.40.tgz", - "integrity": "sha512-BSSLZ9/Cjjv7Gtj5B68ZzXcXUg8iOf3fme+FCuh8rC/Go+Kmh8cox7M3A8dolou16s64QjLPOSdngh7GxXvkSw==", + "version": "2.10.42", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.42.tgz", + "integrity": "sha512-c/jurFrDLyui7o1J86yLkRu4LMsTYcBohveus7/I2Hzdn9KIP2bdJPTue/lR1KH46enoPbD77GKeSYNdyPoD3Q==", "devOptional": true, "license": "Apache-2.0", "bin": { @@ -6521,9 +6521,9 @@ } }, "node_modules/browserslist": { - "version": "4.28.4", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.4.tgz", - "integrity": "sha512-MTc8i/x9jBQd1iMw2CFGS+rwMa07eYjLR0CCTLDACl9xhxy+nIs3KeML/biicXtk9JrZ6dnnTatmc7ErPXIxqw==", + "version": "4.28.5", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.5.tgz", + "integrity": "sha512-Cu2E6QejHWzuDMTkuwgpABFgDfZrXLQq5V13YOACZx4mFAG4IwGTbTfHPMr4WtxlHoXSM8FIuRwYYCz5XiabaQ==", "devOptional": true, "funding": [ { @@ -6541,10 +6541,10 @@ ], "license": "MIT", "dependencies": { - "baseline-browser-mapping": "^2.10.38", - "caniuse-lite": "^1.0.30001799", - "electron-to-chromium": "^1.5.376", - "node-releases": "^2.0.48", + "baseline-browser-mapping": "^2.10.42", + "caniuse-lite": "^1.0.30001800", + "electron-to-chromium": "^1.5.387", + "node-releases": "^2.0.50", "update-browserslist-db": "^1.2.3" }, "bin": { @@ -6623,9 +6623,9 @@ "license": "MIT" }, "node_modules/bullmq": { - "version": "5.79.2", - "resolved": "https://registry.npmjs.org/bullmq/-/bullmq-5.79.2.tgz", - "integrity": "sha512-FebD+8XCZl/hnS1R4to24L4EAN70XSndKZO0776M36vGRk5MKVhKlNFM8/34zXLXKyYB4QaeIPFhVXSYYGTHpQ==", + "version": "5.79.3", + "resolved": "https://registry.npmjs.org/bullmq/-/bullmq-5.79.3.tgz", + "integrity": "sha512-ol/zGeYw1AOKVWCgqGbElGAcvpO8ijc0qmTigXveEBOh+6yCiSwAG3ISI8zYW9Yu3sijnt58O/3JNtJGUYebLA==", "license": "MIT", "dependencies": { "cron-parser": "4.9.0", @@ -6963,9 +6963,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001800", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001800.tgz", - "integrity": "sha512-MMHtuAz9Ys840zAY5F4k6fV5GaivZ9sPk+nz0mY+GYVzRBnYkN0mpqkSR92oWRQ19yQWo4HvBV/FnC16AJX8MA==", + "version": "1.0.30001803", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001803.tgz", + "integrity": "sha512-g/uHREV2ZpK9qMalCsWaxmA6ol+DX8GYhuf3T40RKoP+oL7vhRJh8LNt73PCjpnR6l14FzfPrB5Yux4PKm2meg==", "devOptional": true, "funding": [ { @@ -8394,9 +8394,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.384", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.384.tgz", - "integrity": "sha512-g6KAKY1vkYsADvSPWvdJsuYT0ixdcu6lUtD9P/wJKGBEDlZVXh2AX42j1mPqqaQPDluWjara9ziQ7xqAeXCt5A==", + "version": "1.5.388", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.388.tgz", + "integrity": "sha512-Pl/aJaqOOxYxda3vcx1IKSJimwYXHDkEnGn0F+kG2EE68dDtx2uCinaS+Vih8Z91B9t8CSAbiF/HKyWcnXjhzw==", "devOptional": true, "license": "ISC" }, @@ -8579,9 +8579,9 @@ } }, "node_modules/enhanced-resolve": { - "version": "5.24.1", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.24.1.tgz", - "integrity": "sha512-7DdUaTjmNwMcH2gLr1qycesKII3BK4RLy/mdAb7x10Lq7bR4aNKHt1BR1ZALSv0rPM/hF5wYF0PhGop/rJm8vw==", + "version": "5.24.2", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.24.2.tgz", + "integrity": "sha512-rpsZEGT1jFuve6QlpyRp9ckQ+kN61hvF9BzCPyMdaKTm8UJce96KBn3sorXOFXlzjPrs3Vc4T1NsSroZ3PxlFw==", "dev": true, "license": "MIT", "dependencies": { @@ -10525,9 +10525,9 @@ } }, "node_modules/iconv-lite": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", - "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.3.tgz", + "integrity": "sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ==", "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" @@ -12215,22 +12215,22 @@ "optional": true }, "node_modules/libmime": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/libmime/-/libmime-5.4.0.tgz", - "integrity": "sha512-MAWyU2qYtCsrZ6GClgukz2jx73NQrzA6ASo9qzThuTG+A7+H3lWQuBsjoy9lls+v6q/epAeBdEMJ3T0f4b+uRw==", + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/libmime/-/libmime-5.4.1.tgz", + "integrity": "sha512-0wHGhsofo9IdQPenr3BBHXuxcwMq4atFUTsZ9Ogc1OvI5h4rUdDIrBQEN9JHjCXfDMrE59LUMJWsTD82wTYk8A==", "license": "MIT", "optional": true, "dependencies": { "encoding-japanese": "2.2.0", - "iconv-lite": "0.7.2", + "iconv-lite": "0.7.3", "libbase64": "1.3.0", "libqp": "2.1.1" } }, "node_modules/libphonenumber-js": { - "version": "1.13.7", - "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.13.7.tgz", - "integrity": "sha512-rvr3HIMdOgzhz1RFGjftji+wjoAFlzhqCNqJOU/MKTZQ8d9NZxAR/tI+0weDicyoucqVR0U1GCniqHJ0f8aM2A==", + "version": "1.13.8", + "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.13.8.tgz", + "integrity": "sha512-80xal1m93rADejw2pMp2MSzFhHCPLEspjHxnH2UtqI+DgAmElsbmLMiqk9niwH9NWAfjsRtaJI+qBrOEmRx9nQ==", "license": "MIT" }, "node_modules/libqp": { @@ -12280,9 +12280,9 @@ "license": "MIT" }, "node_modules/linkify-it": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.1.tgz", - "integrity": "sha512-wVoTjP4Q6R0NW5hiZkVJaFZPWgtXfoGF+6LucL3/FtiNjmcHhYjEr5f1Kqjirc1nBW07J/ZuRFumqr2oqccEWg==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.2.tgz", + "integrity": "sha512-ONTm2jCMAVZjgQa/Fy1kScXsuOoF5NPTsoFBdE1KVIZ2vAh/r9+Bqo+0jINCBYnavTPQZz38QzFTme79ENoN3Q==", "funding": [ { "type": "github", @@ -12537,34 +12537,24 @@ } }, "node_modules/mailparser": { - "version": "3.9.12", - "resolved": "https://registry.npmjs.org/mailparser/-/mailparser-3.9.12.tgz", - "integrity": "sha512-kbT4xtkKEddonhDTjjWWVFJlI5axm0S9QuIAHs1ue3IEw+fNd9o4ytPKaG7p1LOE2aKifrWLjx/omOGLHz/9RQ==", + "version": "3.9.14", + "resolved": "https://registry.npmjs.org/mailparser/-/mailparser-3.9.14.tgz", + "integrity": "sha512-3QD6TRXcyXtq2NCuyA2AEjqmallQkyxYmZI9GMCIvQDCaB9Uc034WUI1x8RUBYFnk5+p7h14JEz4O/lrxQmttw==", "license": "MIT", "optional": true, "dependencies": { - "@zone-eu/mailsplit": "5.4.13", + "@zone-eu/mailsplit": "5.4.14", "encoding-japanese": "2.2.0", "he": "1.2.0", "html-to-text": "10.0.0", - "iconv-lite": "0.7.2", - "libmime": "5.4.0", - "linkify-it": "5.0.1", - "nodemailer": "9.0.1", + "iconv-lite": "0.7.3", + "libmime": "5.4.1", + "linkify-it": "5.0.2", + "nodemailer": "9.0.3", "punycode.js": "2.3.1", "tlds": "1.261.0" } }, - "node_modules/mailparser/node_modules/nodemailer": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-9.0.1.tgz", - "integrity": "sha512-Gwv8SQewT616ZM/URn0H54b8PWo/Wum7md3EW2aWy1lO27+WZCX+Xyak3J+NlmHUjDh5ME+uesJUDRbR3Ye8Bw==", - "license": "MIT-0", - "optional": true, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/make-dir": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", @@ -13638,9 +13628,9 @@ "license": "MIT" }, "node_modules/node-abi": { - "version": "3.93.0", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.93.0.tgz", - "integrity": "sha512-Cu6yUpX5Iavugm8BeX7c0wgU9CvOqfd1yM6A1d2q2ZMjym7GjpASv2GdRcTq3Fx+Sb5OgBkEEpw4VnAbY6Y5RA==", + "version": "3.94.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.94.0.tgz", + "integrity": "sha512-W5ZNO5KRPB5TkYmGVD9F6YqhsglXJzE6etpbmT+f6EQElhiX/UTG551cnsRGvLG3fyZEg9HwaDmNmj5nwJ4z9g==", "license": "MIT", "dependencies": { "semver": "^7.3.5" @@ -18678,9 +18668,9 @@ "license": "BSD-2-Clause" }, "node_modules/webpack": { - "version": "5.108.3", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.108.3.tgz", - "integrity": "sha512-hOpaCHmQVVY66IVTjofnH14IgSdmod2aquSGHGuYig/OIdWge01Hk2Wt988DZcwXumFUT4+FvJY5N+ikl8o/ww==", + "version": "5.108.4", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.108.4.tgz", + "integrity": "sha512-yur8LyJoeiWh47dErD+Ok7vlbmDsJ3UbbRPAoxbGJ54WpE2y5yVo5G/inUzujnYgw3tPmBRdn+G7PoxXaYC33w==", "dev": true, "license": "MIT", "peer": true, @@ -18735,9 +18725,9 @@ } }, "node_modules/webpack-sources": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.5.0.tgz", - "integrity": "sha512-HPuy+uuoTCaaoEoI1LQ3JN9+vrPBvEesnnX1jADHy728cHSMlq4wUc4afYqahq2B1mhQVZxCXOkNTnXltr+2vQ==", + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.5.1.tgz", + "integrity": "sha512-jyuiGJdtvY434z5bUZrjz67v76/ePNvFZTp9Mdz29IlH4+GPsgyGjiv0fKI+M7BdkU6ADjulUcKAd3tUK3WlEw==", "dev": true, "license": "MIT", "engines": { diff --git a/src/auth/auth.service.ts b/src/auth/auth.service.ts index 0e641fe..7617781 100644 --- a/src/auth/auth.service.ts +++ b/src/auth/auth.service.ts @@ -1384,23 +1384,25 @@ export class AuthService { // Generate new reset token const resetToken = randomToken(32); + const tokenHash = createSha256(resetToken); const expiresAt = new Date(Date.now() + 60 * 60 * 1000); // 1 hour await this.prisma.passwordResetToken.create({ data: { userId: user.id, - token: resetToken, + token: tokenHash, expiresAt, }, }); - // Send reset email + // Send reset email with raw token (not the hash) await this.emailService.sendPasswordResetEmail(user.email, resetToken); } async resetPassword(data: ResetPasswordDto): Promise { + const tokenHash = createSha256(data.token); const resetToken = await this.prisma.passwordResetToken.findUnique({ - where: { token: data.token }, + where: { token: tokenHash }, include: { user: true }, }); @@ -1530,8 +1532,11 @@ export class AuthService { private async verifyCaptcha(token: string): Promise { const secret = this.configService.get('RECAPTCHA_SECRET'); if (!secret) { + if (process.env.NODE_ENV === 'production') { + throw new Error('RECAPTCHA_SECRET is not configured in production'); + } this.logger.warn('RECAPTCHA_SECRET is not configured, skipping CAPTCHA verification'); - return true; // Bypass if not configured in dev + return true; // Bypass only in non-production } try { diff --git a/src/main.ts b/src/main.ts index eb1d837..c20f63a 100644 --- a/src/main.ts +++ b/src/main.ts @@ -20,62 +20,12 @@ async function bootstrap() { const logger = new Logger('Bootstrap'); // Node.js version check (#775, #754 NestJS 11 requires Node 20+) + const REQUIRED_NODE_MAJOR = 20; const nodeMajor = parseInt(process.versions.node.split('.')[0], 10); - if (nodeMajor < 20) { - logger.error(`Node.js >= 20 required (NestJS 11), found ${process.versions.node}`); - // Node.js version check (#775): - // package.json declares engines.node >= 18, but several transitive - // dependencies (e.g. @nestjs/* v11) require Node 20+. Enforce that here - // and exit early with a clear message well before any module loads. - const nodeMajor = parseInt(process.versions.node.split('.')[0], 10); - const REQUIRED_NODE_MAJOR = 20; - if (Number.isNaN(nodeMajor) || nodeMajor < REQUIRED_NODE_MAJOR) { - logger.error( - `Node.js >= ${REQUIRED_NODE_MAJOR} required, found ${process.versions.node}. ` + - `Please upgrade Node.js (see https://nodejs.org/).`, - ); - process.exit(1); - } - - const app = await NestFactory.create(AppModule); - - // Enable CORS - app.enableCors(); - - // Global prefix - app.setGlobalPrefix('api'); - - // Get services for guard initialization - const reflector = app.get(Reflector); - const rateLimitService = app.get(RateLimitService); - - // Apply global guards - app.useGlobalGuards(new RateLimitGuard(reflector, rateLimitService)); - - // Apply version header interceptor globally - app.useGlobalInterceptors(new VersionHeaderInterceptor()); - - // Apply deprecation warning interceptor - app.useGlobalInterceptors(new DeprecationWarningInterceptor(reflector)); - - // Apply rate limit headers interceptor - app.useGlobalInterceptors(new RateLimitHeadersInterceptor()); - - // Apply cache metrics interceptor - // Retrieve the singleton instance from the DI container to ensure consistent dependency injection - const cacheMetricsInterceptor = app.get(CacheMetricsInterceptor); - app.useGlobalInterceptors(cacheMetricsInterceptor); - - // Enable a single ValidationPipe with implicit conversion (#754 NestJS 11 upgrade) - app.useGlobalPipes( - new ValidationPipe({ - whitelist: true, - forbidNonWhitelisted: true, - transform: true, - transformOptions: { - enableImplicitConversion: true, - }, - }), + if (Number.isNaN(nodeMajor) || nodeMajor < REQUIRED_NODE_MAJOR) { + logger.error( + `Node.js >= ${REQUIRED_NODE_MAJOR} required, found ${process.versions.node}. ` + + `Please upgrade Node.js (see https://nodejs.org/).`, ); // Setup Swagger documentation diff --git a/src/properties/properties.controller.ts b/src/properties/properties.controller.ts index ebbf030..eb8d2c2 100644 --- a/src/properties/properties.controller.ts +++ b/src/properties/properties.controller.ts @@ -167,6 +167,8 @@ export class PropertiesController { return this.propertiesService.rejectProperty(id, user.sub); } + @UseGuards(JwtAuthGuard, RolesGuard) + @Roles(UserRole.ADMIN) @Post('bulk/status') async bulkUpdatePropertyStatus( @Body() body: BulkPropertyStatusUpdateDto, @@ -175,6 +177,8 @@ export class PropertiesController { return this.propertiesService.bulkUpdatePropertyStatus(body.propertyIds, body.status); } + @UseGuards(JwtAuthGuard, RolesGuard) + @Roles(UserRole.ADMIN) @Post('bulk/delete') async bulkDeleteProperties( @Body() body: BulkPropertyDeleteDto, @@ -183,6 +187,8 @@ export class PropertiesController { return this.propertiesService.bulkDeleteProperties(body.propertyIds); } + @UseGuards(JwtAuthGuard, RolesGuard) + @Roles(UserRole.ADMIN) @Post('bulk/export') async bulkExportProperties( @Body() body: BulkPropertyExportDto,