Finaliza roles, add compose

This commit is contained in:
LeoMortari
2025-09-14 01:50:04 -03:00
parent 7b7a666902
commit 63a4a002ec
7 changed files with 105 additions and 15 deletions

View File

@@ -1,3 +1 @@
DATABASE_URL="postgresql://username:password@ip_server:port/database?schema=public"
REDIS_URL="redis://username:password@ip_server:port"
SESSION_TTL_SECONDS=3600

17
.github/workflows/build.yml vendored Normal file
View File

@@ -0,0 +1,17 @@
# name: Build
# on:
# push:
# branches: [master]
# jobs:
# build:
# name: Build
# runs-on: ubuntu-latest
# steps:
# - uses: actions/checkout@v3
# - name: Call build in Dokploy
# if: success()
# run: curl -X GET ${{ secrets.BUILD_WEBHOOK_URL }}

30
Dockerfile Normal file
View File

@@ -0,0 +1,30 @@
FROM node:22-alpine AS builder
WORKDIR /usr/src/app
RUN apk add --no-cache python3 make g++
COPY package.json ./
COPY prisma ./prisma/
RUN yarn install --frozen-lockfile
COPY . .
RUN npx prisma generate
RUN yarn build
FROM node:22-alpine
WORKDIR /usr/src/app
COPY --from=builder /usr/src/app/package.json ./
COPY --from=builder /usr/src/app/node_modules ./node_modules
COPY --from=builder /usr/src/app/dist ./dist
COPY --from=builder /usr/src/app/prisma ./prisma
EXPOSE 3000
CMD ["yarn", "start:prod"]

24
docker-compose.yml Normal file
View File

@@ -0,0 +1,24 @@
services:
app:
build:
context: .
dockerfile: Dockerfile
container_name: clipperia-api
ports:
- '3050:3000'
environment:
NODE_ENV: production
DATABASE_URL: postgresql://postgres:postgres@db:5432/clipperia?schema=public
volumes:
- .:/usr/src/app
- /usr/src/app/node_modules
networks:
- clipperia-network
# - dokploy-network
command: ['yarn', 'start:prod']
networks:
clipperia-network:
internal: true
dokploy-network:
external: true

View File

@@ -9,9 +9,9 @@
"build": "nest build",
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
"start": "nest start",
"start:dev": "NODE_ENV=development nest start --watch",
"start:debug": "NODE_ENV=development nest start --debug --watch",
"start:prod": "NODE_ENV=production node dist/main",
"start:dev": "cross-env NODE_ENV=development nest start --watch",
"start:debug": "cross-env NODE_ENV=development nest start --debug --watch",
"start:prod": "cross-env NODE_ENV=production node dist/main",
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
"test": "jest",
"test:watch": "jest --watch",
@@ -30,6 +30,7 @@
"bcrypt": "6.0.0",
"class-transformer": "0.5.1",
"class-validator": "0.14.2",
"cross-env": "10.0.0",
"dayjs": "1.11.13",
"jwks-rsa": "3.2.0",
"passport": "0.7.0",

View File

@@ -1,6 +1,5 @@
import { Module, MiddlewareConsumer } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { APP_GUARD } from '@nestjs/core';
import { PrismaModule } from './prisma/prisma.module';
import { AppController } from './app.controller';
import { AppService } from './app.service';
@@ -20,7 +19,7 @@ import { RolesGuard } from './auth/roles.guard';
UsuariosModule,
],
controllers: [AppController],
providers: [AppService, { provide: APP_GUARD, useClass: RolesGuard }],
providers: [AppService, RolesGuard],
})
export class AppModule {
configure(consumer: MiddlewareConsumer) {

View File

@@ -13,27 +13,48 @@ import type { JwtPayload } from './keycloak.strategy';
export class RolesGuard implements CanActivate {
constructor(private reflector: Reflector) {}
private extractRoles(user: JwtPayload): string[] {
const roles: string[] = [];
if (user.realm_access?.roles) {
roles.push(...user.realm_access.roles);
}
if (user.resource_access) {
Object.values(user.resource_access).forEach((resource) => {
if (resource?.roles) {
roles.push(...resource.roles);
}
});
}
return [...new Set(roles)];
}
canActivate(context: ExecutionContext): boolean {
const requiredRoles = this.reflector.getAllAndOverride<string[]>(
ROLES_KEY,
[context.getHandler(), context.getClass()],
);
if (!requiredRoles || requiredRoles.length === 0) {
return true;
if (!requiredRoles || !requiredRoles.length) {
return false;
}
const request = context.switchToHttp().getRequest<{ user: JwtPayload }>();
const user = request.user;
const userRoles: string[] = [
...(user?.resource_access?.clipperia?.roles || []),
];
if (!user) {
throw new ForbiddenException('Usuário não autenticado');
}
const userRoles = this.extractRoles(user);
console.log(context);
const hasRole = requiredRoles.some((role) => userRoles.includes(role));
if (!hasRole) {
throw new ForbiddenException(
'O usuário não possui permissão para acessar esta rota',
'Você não possui permissão para acessar este recurso',
);
}