fix(security): rate limiting, forbidNonWhitelisted, Prisma migrations en startup y CI para GHCR
- 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 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
1b49e3fc0e
commit
db19f3e1ca
@@ -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
|
||||||
+2
-1
@@ -19,4 +19,5 @@ COPY --from=builder /app/node_modules ./node_modules
|
|||||||
COPY --from=builder /app/packages/database ./packages/database
|
COPY --from=builder /app/packages/database ./packages/database
|
||||||
USER app
|
USER app
|
||||||
EXPOSE 3001
|
EXPOSE 3001
|
||||||
CMD ["node", "dist/main.js"]
|
# 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"]
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
import { Module } from "@nestjs/common";
|
import { Module } from "@nestjs/common";
|
||||||
import { ConfigModule } from "@nestjs/config";
|
import { ConfigModule } from "@nestjs/config";
|
||||||
|
import { ThrottlerModule, ThrottlerGuard } from "@nestjs/throttler";
|
||||||
|
import { APP_GUARD } from "@nestjs/core";
|
||||||
import { HealthModule } from "./health/health.module";
|
import { HealthModule } from "./health/health.module";
|
||||||
import { PrismaModule } from "./prisma/prisma.module";
|
import { PrismaModule } from "./prisma/prisma.module";
|
||||||
import { CalculatorModule } from "./calculator/calculator.module";
|
import { CalculatorModule } from "./calculator/calculator.module";
|
||||||
@@ -25,6 +27,7 @@ import { ConsolidationsModule } from "./consolidations/consolidations.module";
|
|||||||
isGlobal: true,
|
isGlobal: true,
|
||||||
envFilePath: ["../../.env", ".env"],
|
envFilePath: ["../../.env", ".env"],
|
||||||
}),
|
}),
|
||||||
|
ThrottlerModule.forRoot([{ ttl: 60000, limit: 20 }]),
|
||||||
PrismaModule,
|
PrismaModule,
|
||||||
HealthModule,
|
HealthModule,
|
||||||
CalculatorModule,
|
CalculatorModule,
|
||||||
@@ -44,5 +47,6 @@ import { ConsolidationsModule } from "./consolidations/consolidations.module";
|
|||||||
PaymentsModule,
|
PaymentsModule,
|
||||||
ConsolidationsModule,
|
ConsolidationsModule,
|
||||||
],
|
],
|
||||||
|
providers: [{ provide: APP_GUARD, useClass: ThrottlerGuard }],
|
||||||
})
|
})
|
||||||
export class AppModule {}
|
export class AppModule {}
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ async function bootstrap() {
|
|||||||
app.useGlobalPipes(
|
app.useGlobalPipes(
|
||||||
new ValidationPipe({
|
new ValidationPipe({
|
||||||
whitelist: true,
|
whitelist: true,
|
||||||
forbidNonWhitelisted: false,
|
forbidNonWhitelisted: true,
|
||||||
transform: true,
|
transform: true,
|
||||||
transformOptions: { enableImplicitConversion: true },
|
transformOptions: { enableImplicitConversion: true },
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -47,6 +47,9 @@ services:
|
|||||||
|
|
||||||
web:
|
web:
|
||||||
image: ghcr.io/lizandrogd/moraworld-imports/web:latest
|
image: ghcr.io/lizandrogd/moraworld-imports/web:latest
|
||||||
|
depends_on:
|
||||||
|
api:
|
||||||
|
condition: service_healthy
|
||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
dockerfile: apps/web/Dockerfile
|
dockerfile: apps/web/Dockerfile
|
||||||
|
|||||||
Reference in New Issue
Block a user