diff --git a/package-lock.json b/package-lock.json index 9fc1b4c..4ee336c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "0.0.1", "license": "MIT", "dependencies": { + "@fastify/helmet": "^11.1.1", "@fastify/static": "^6.12.0", "@nestjs/common": "^10.0.0", "@nestjs/config": "^3.1.1", @@ -18,8 +19,11 @@ "@nestjs/platform-express": "^10.0.0", "@nestjs/platform-fastify": "^10.3.1", "@nestjs/swagger": "^7.2.0", + "@nestjs/throttler": "^5.1.1", "@prisma/client": "^5.8.1", "bcrypt": "^5.1.1", + "ioredis": "^5.3.2", + "nestjs-throttler-storage-redis": "^0.4.1", "nestjs-zod": "^3.0.0", "passport": "^0.7.0", "passport-jwt": "^4.0.1", @@ -1240,6 +1244,15 @@ "fastify-plugin": "^4.0.0" } }, + "node_modules/@fastify/helmet": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/@fastify/helmet/-/helmet-11.1.1.tgz", + "integrity": "sha512-pjJxjk6SLEimITWadtYIXt6wBMfFC1I6OQyH/jYVCqSAn36sgAIFjeNiibHtifjCd+e25442pObis3Rjtame6A==", + "dependencies": { + "fastify-plugin": "^4.2.1", + "helmet": "^7.0.0" + } + }, "node_modules/@fastify/middie": { "version": "8.3.0", "resolved": "https://registry.npmjs.org/@fastify/middie/-/middie-8.3.0.tgz", @@ -1376,6 +1389,11 @@ "integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==", "dev": true }, + "node_modules/@ioredis/commands": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.2.0.tgz", + "integrity": "sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg==" + }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -2443,6 +2461,19 @@ } } }, + "node_modules/@nestjs/throttler": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@nestjs/throttler/-/throttler-5.1.1.tgz", + "integrity": "sha512-0fJAGroqpQLnQlERslx2fG264YCXU35nMfiFhykY6/chgc56/W0QPM6BEEf9Q/Uca9lXh5IyjE0fqFToksbP/A==", + "dependencies": { + "md5": "^2.2.1" + }, + "peerDependencies": { + "@nestjs/common": "^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0", + "@nestjs/core": "^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0", + "reflect-metadata": "^0.1.13 || ^0.2.0" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -4046,6 +4077,14 @@ "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", "dev": true }, + "node_modules/charenc": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", + "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==", + "engines": { + "node": "*" + } + }, "node_modules/chokidar": { "version": "3.5.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", @@ -4214,6 +4253,14 @@ "node": ">=0.10.0" } }, + "node_modules/cluster-key-slot": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", + "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", @@ -4457,6 +4504,14 @@ "node": ">= 8" } }, + "node_modules/crypt": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", + "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==", + "engines": { + "node": "*" + } + }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -4541,6 +4596,14 @@ "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==" }, + "node_modules/denque": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", + "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==", + "engines": { + "node": ">=0.10" + } + }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -5954,6 +6017,14 @@ "node": ">= 0.4" } }, + "node_modules/helmet": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/helmet/-/helmet-7.1.0.tgz", + "integrity": "sha512-g+HZqgfbpXdCkme/Cd/mZkV0aV3BZZZSugecH03kl38m/Kmdx8jKjBikpDj2cr+Iynv4KpYEviojNdTJActJAg==", + "engines": { + "node": ">=16.0.0" + } + }, "node_modules/hexoid": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/hexoid/-/hexoid-1.0.0.tgz", @@ -6137,6 +6208,29 @@ "node": ">= 0.10" } }, + "node_modules/ioredis": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.3.2.tgz", + "integrity": "sha512-1DKMMzlIHM02eBBVOFQ1+AolGjs6+xEcM4PDL7NqOS6szq7H9jSaEkIUH6/a5Hl241LzW6JLSiAbNvTQjUupUA==", + "dependencies": { + "@ioredis/commands": "^1.1.1", + "cluster-key-slot": "^1.1.0", + "debug": "^4.3.4", + "denque": "^2.1.0", + "lodash.defaults": "^4.2.0", + "lodash.isarguments": "^3.1.0", + "redis-errors": "^1.2.0", + "redis-parser": "^3.0.0", + "standard-as-callback": "^2.1.0" + }, + "engines": { + "node": ">=12.22.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/ioredis" + } + }, "node_modules/ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", @@ -7317,11 +7411,21 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, + "node_modules/lodash.defaults": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", + "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==" + }, "node_modules/lodash.includes": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" }, + "node_modules/lodash.isarguments": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", + "integrity": "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==" + }, "node_modules/lodash.isboolean": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", @@ -7431,6 +7535,16 @@ "tmpl": "1.0.5" } }, + "node_modules/md5": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", + "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==", + "dependencies": { + "charenc": "0.0.2", + "crypt": "0.0.2", + "is-buffer": "~1.1.6" + } + }, "node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -7703,6 +7817,18 @@ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", "dev": true }, + "node_modules/nestjs-throttler-storage-redis": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/nestjs-throttler-storage-redis/-/nestjs-throttler-storage-redis-0.4.1.tgz", + "integrity": "sha512-nSjPWfzAqwTFl6WA+wqwjJIHf63ffG9RJTojccHrDIE6cEk5Iw5IjO98crcBbu4PEqezWoX2eaUwAtpB1Ei8jA==", + "peerDependencies": { + "@nestjs/common": "^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0", + "@nestjs/core": "^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0", + "@nestjs/throttler": ">=5.0.0", + "ioredis": ">=5.0.0", + "reflect-metadata": "^0.1.13" + } + }, "node_modules/nestjs-zod": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/nestjs-zod/-/nestjs-zod-3.0.0.tgz", @@ -8602,6 +8728,25 @@ "node": ">= 0.10" } }, + "node_modules/redis-errors": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", + "integrity": "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==", + "engines": { + "node": ">=4" + } + }, + "node_modules/redis-parser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", + "integrity": "sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==", + "dependencies": { + "redis-errors": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/reflect-metadata": { "version": "0.1.14", "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.14.tgz", @@ -9272,6 +9417,11 @@ "node": ">=8" } }, + "node_modules/standard-as-callback": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz", + "integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==" + }, "node_modules/statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", diff --git a/package.json b/package.json index d6cd81a..444285f 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ "test:watch": "jest --watch" }, "dependencies": { + "@fastify/helmet": "^11.1.1", "@fastify/static": "^6.12.0", "@nestjs/common": "^10.0.0", "@nestjs/config": "^3.1.1", @@ -37,8 +38,11 @@ "@nestjs/platform-express": "^10.0.0", "@nestjs/platform-fastify": "^10.3.1", "@nestjs/swagger": "^7.2.0", + "@nestjs/throttler": "^5.1.1", "@prisma/client": "^5.8.1", "bcrypt": "^5.1.1", + "ioredis": "^5.3.2", + "nestjs-throttler-storage-redis": "^0.4.1", "nestjs-zod": "^3.0.0", "passport": "^0.7.0", "passport-jwt": "^4.0.1", diff --git a/src/app.module.ts b/src/app.module.ts index c6c22ca..8b2ac0c 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -6,6 +6,8 @@ import { PostModule } from "./post/post.module"; import { AuthModule } from "./auth/auth.module"; import { ConfigModule } from "@nestjs/config"; import { JwtAuthGuard } from "./auth/jwt-auth.guard"; +import { ThrottlerGuard, ThrottlerModule } from "@nestjs/throttler"; +import { ThrottlerStorageRedisService } from "nestjs-throttler-storage-redis"; @Module({ imports: [ @@ -15,6 +17,12 @@ import { JwtAuthGuard } from "./auth/jwt-auth.guard"; ConfigModule.forRoot({ isGlobal: true, }), + ThrottlerModule.forRoot({ + throttlers: [{ limit: 10, ttl: 60000 }], + storage: new ThrottlerStorageRedisService( + `redis://:${process.env.REDIS_PASSWORD}@${process.env.REDIS_HOST}:${process.env.REDIS_PORT}/0`, + ), + }), ], providers: [ { @@ -25,6 +33,10 @@ import { JwtAuthGuard } from "./auth/jwt-auth.guard"; provide: APP_GUARD, useClass: JwtAuthGuard, }, + { + provide: APP_GUARD, + useClass: ThrottlerGuard, + }, ], }) export class AppModule {} diff --git a/src/main.ts b/src/main.ts index 5632ab5..9d00192 100644 --- a/src/main.ts +++ b/src/main.ts @@ -6,6 +6,7 @@ import { FastifyAdapter, NestFastifyApplication, } from "@nestjs/platform-fastify"; +import * as helmet from "@fastify/helmet"; async function bootstrap() { const app = await NestFactory.create( @@ -42,6 +43,8 @@ async function bootstrap() { SwaggerModule.setup("/", app, document); + await app.register(helmet); + await app.listen(3000, "0.0.0.0"); } bootstrap();