From db19f3e1ca8ccfd8615e74f3cf83775f1ff0faf2 Mon Sep 17 00:00:00 2001 From: Lizandro Guarnizo <77708265+lizandrogd@users.noreply.github.com> Date: Thu, 18 Jun 2026 17:06:35 -0500 Subject: [PATCH] fix(security): rate limiting, forbidNonWhitelisted, Prisma migrations en startup y CI para GHCR MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ThrottlerGuard global: 20 req/min por IP en todos los endpoints - forbidNonWhitelisted: true en ValidationPipe - Dockerfile API: prisma migrate deploy antes de arrancar el proceso - coolify-compose: depends_on api (service_healthy) en web - GitHub Actions: build y push de imágenes api/web a GHCR en cada push a main Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/docker-publish.yml | 59 ++++++++++++++++++++++++++++ apps/api/Dockerfile | 3 +- apps/api/src/app.module.ts | 4 ++ apps/api/src/main.ts | 2 +- coolify-compose.yml | 3 ++ 5 files changed, 69 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/docker-publish.yml diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml new file mode 100644 index 0000000..a7b5de7 --- /dev/null +++ b/.github/workflows/docker-publish.yml @@ -0,0 +1,59 @@ +name: Build & Push Docker images + +on: + push: + branches: [main] + workflow_dispatch: + +env: + REGISTRY: ghcr.io + IMAGE_PREFIX: ghcr.io/${{ github.repository_owner }}/moraworld-imports + +jobs: + build-api: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + steps: + - uses: actions/checkout@v4 + + - name: Log in to GHCR + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build & push API + uses: docker/build-push-action@v6 + with: + context: . + file: apps/api/Dockerfile + push: true + tags: ${{ env.IMAGE_PREFIX }}/api:latest + + build-web: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + steps: + - uses: actions/checkout@v4 + + - name: Log in to GHCR + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build & push Web + uses: docker/build-push-action@v6 + with: + context: . + file: apps/web/Dockerfile + push: true + build-args: | + NEXT_PUBLIC_API_URL=${{ vars.NEXT_PUBLIC_API_URL }} + tags: ${{ env.IMAGE_PREFIX }}/web:latest diff --git a/apps/api/Dockerfile b/apps/api/Dockerfile index a9778ab..df6be4b 100644 --- a/apps/api/Dockerfile +++ b/apps/api/Dockerfile @@ -19,4 +19,5 @@ COPY --from=builder /app/node_modules ./node_modules COPY --from=builder /app/packages/database ./packages/database USER app EXPOSE 3001 -CMD ["node", "dist/main.js"] \ No newline at end of file +# ponytail: migrate antes de arrancar; si quieres zero-downtime migrations, sepáralas como job previo en Coolify +CMD ["sh", "-c", "node node_modules/.bin/prisma migrate deploy --schema packages/database/prisma/schema.prisma && node dist/main.js"] \ No newline at end of file diff --git a/apps/api/src/app.module.ts b/apps/api/src/app.module.ts index 0bffed2..4a95fdd 100644 --- a/apps/api/src/app.module.ts +++ b/apps/api/src/app.module.ts @@ -1,5 +1,7 @@ import { Module } from "@nestjs/common"; import { ConfigModule } from "@nestjs/config"; +import { ThrottlerModule, ThrottlerGuard } from "@nestjs/throttler"; +import { APP_GUARD } from "@nestjs/core"; import { HealthModule } from "./health/health.module"; import { PrismaModule } from "./prisma/prisma.module"; import { CalculatorModule } from "./calculator/calculator.module"; @@ -25,6 +27,7 @@ import { ConsolidationsModule } from "./consolidations/consolidations.module"; isGlobal: true, envFilePath: ["../../.env", ".env"], }), + ThrottlerModule.forRoot([{ ttl: 60000, limit: 20 }]), PrismaModule, HealthModule, CalculatorModule, @@ -44,5 +47,6 @@ import { ConsolidationsModule } from "./consolidations/consolidations.module"; PaymentsModule, ConsolidationsModule, ], + providers: [{ provide: APP_GUARD, useClass: ThrottlerGuard }], }) export class AppModule {} diff --git a/apps/api/src/main.ts b/apps/api/src/main.ts index 5eada06..6b5aa68 100644 --- a/apps/api/src/main.ts +++ b/apps/api/src/main.ts @@ -30,7 +30,7 @@ async function bootstrap() { app.useGlobalPipes( new ValidationPipe({ whitelist: true, - forbidNonWhitelisted: false, + forbidNonWhitelisted: true, transform: true, transformOptions: { enableImplicitConversion: true }, }), diff --git a/coolify-compose.yml b/coolify-compose.yml index 81421d8..a4d6de9 100644 --- a/coolify-compose.yml +++ b/coolify-compose.yml @@ -47,6 +47,9 @@ services: web: image: ghcr.io/lizandrogd/moraworld-imports/web:latest + depends_on: + api: + condition: service_healthy build: context: . dockerfile: apps/web/Dockerfile