Skip to content

Implementação desafio#35

Open
douglasgalvao wants to merge 26 commits intonewsunenergy:mainfrom
douglasgalvao:dev
Open

Implementação desafio#35
douglasgalvao wants to merge 26 commits intonewsunenergy:mainfrom
douglasgalvao:dev

Conversation

@douglasgalvao
Copy link

:)

Douglas Machado added 26 commits February 19, 2026 12:20
Copilot AI review requested due to automatic review settings February 19, 2026 19:48
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR implements a complete fullstack energy compensation simulation system with a NestJS backend following Clean Architecture and DDD principles, and a Next.js 14 frontend. The system allows users to upload energy bill PDFs which are processed by an external API, storing lead information with consumption history in a MySQL database.

Changes:

  • Complete backend implementation with Clean Architecture (domain, application, infrastructure, presentation layers)
  • Frontend with Next.js 14 App Router, TailwindCSS, and form validation
  • Docker compose setup with MySQL, backend, and frontend services with health checks
  • Comprehensive documentation (README.md and PROJECT.md)

Reviewed changes

Copilot reviewed 69 out of 72 changed files in this pull request and generated 14 comments.

Show a summary per file
File Description
backend/src/domain/ Domain entities (Lead, Unidade, Consumo) with validation and business rules
backend/src/application/ Use cases for creating, listing, and fetching leads with DTOs
backend/src/infra/ Prisma repositories, external Magic PDF service integration, global filters/interceptors
backend/src/presentation/ REST controllers with file upload handling and validation DTOs
backend/prisma/schema.prisma Database schema with Lead, Unidade, and Consumo tables
frontend/src/app/ Pages for simulation form (/simular) and lead listing (/listagem)
frontend/src/components/ Reusable LoadingSpinner and Alert components
frontend/src/services/ API integration service using axios
frontend/src/types/ TypeScript type definitions matching backend DTOs
docker-compose.yml Multi-container setup with MySQL, backend (port 3001), and frontend (port 3000)
README.md & PROJECT.md Comprehensive documentation with setup instructions and architecture details
Comments suppressed due to low confidence (1)

frontend/src/app/listagem/page.tsx:37

  • Debug console.log statements: These debugging console.log statements on lines 36-37 should be removed before production deployment. Consider using a proper logging library or removing them entirely for production code.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +51 to +53
} else {
message = exception.message;
}
Copy link

Copilot AI Feb 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Information disclosure risk: On line 52, generic Error exceptions expose their raw error messages to the client without sanitization. This could potentially leak sensitive information like database connection strings, internal file paths, or stack traces in production. In production mode (NODE_ENV === 'production'), generic errors should return a safe generic message like "Erro interno do servidor" instead of exception.message. Only known domain exceptions should expose their messages to clients.

Copilot uses AI. Check for mistakes.
historicoDeConsumoEmKWH: pdfData.consumption_history.map(
(item) => ({
consumoForaPontaEmKWH: item.consumo_fp,
mesDoConsumo: new Date(item.consumo_date),
Copy link

Copilot AI Feb 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Potential invalid date handling: Line 64 creates a Date object from item.consumo_date without validating if it's a valid date string. If the external magic-pdf API returns an invalid date format, this could create an Invalid Date object which would pass through to the Consumo entity validation. Consider adding date validation or wrapping this in a try-catch to provide a clearer error message if the date parsing fails.

Copilot uses AI. Check for mistakes.
- id: UUID
- codigoDaUnidadeConsumidora: string (unique)
- modeloFasico: 'monofasico' | 'bifasico' | 'trifasico'
- enquadramento: 'AX' | 'B1' | 'B2' | 'B3'
Copy link

Copilot AI Feb 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Documentation error: The enquadramento type should be 'A4' | 'B1' | 'B2' | 'B3', not 'AX' | 'B1' | 'B2' | 'B3'. The value 'AX' is incorrect and inconsistent with the actual implementation which uses 'A4' throughout the codebase (see backend/src/domain/entities/unidade.entity.ts, backend/src/infra/external/magic-pdf.service.ts, and frontend/src/types/lead.ts).

Suggested change
- enquadramento: 'AX' | 'B1' | 'B2' | 'B3'
- enquadramento: 'A4' | 'B1' | 'B2' | 'B3'

Copilot uses AI. Check for mistakes.
Comment on lines +14 to +16
const unidades = prismaLead.unidades.map((prismaUnidade: any) => {
const consumos = prismaUnidade.historicoDeConsumo.map(
(prismaConsumo: any) =>
Copy link

Copilot AI Feb 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ESLint rule violation: The code uses 'any' types despite the ESLint rule "@typescript-eslint/no-explicit-any": "error" being set. These explicit 'any' types on lines 14 and 16 should be replaced with proper types. For prismaUnidade, use the PrismaUnidade type from the PrismaLeadWithRelations type. For prismaConsumo, use PrismaConsumo.

Copilot uses AI. Check for mistakes.
Comment on lines +10 to +12
super({
log: ['query', 'error', 'warn'],
});
Copy link

Copilot AI Feb 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Performance and security concern: Prisma is configured to log all queries in production (line 11: log: ['query', 'error', 'warn']). Logging every query in production can significantly impact performance and expose sensitive data in logs. Consider disabling query logging in production or making it configurable based on NODE_ENV. Change to: log: process.env.NODE_ENV === 'production' ? ['error', 'warn'] : ['query', 'error', 'warn']

Copilot uses AI. Check for mistakes.
Comment on lines +1 to +27
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
'./src/pages/**/*.{js,ts,jsx,tsx,mdx}',
'./src/components/**/*.{js,ts,jsx,tsx,mdx}',
'./src/app/**/*.{js,ts,jsx,tsx,mdx}',
],
theme: {
extend: {
colors: {
primary: {
50: '#f0f9ff',
100: '#e0f2fe',
200: '#bae6fd',
300: '#7dd3fc',
400: '#38bdf8',
500: '#0ea5e9',
600: '#0284c7',
700: '#0369a1',
800: '#075985',
900: '#0c4a6e',
},
},
},
},
plugins: [],
}
Copy link

Copilot AI Feb 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicate configuration files: Both tailwind.config.js and tailwind.config.ts are present. This can cause configuration conflicts and confusion. Choose one format (preferably tailwind.config.ts for TypeScript consistency) and remove the other.

Copilot uses AI. Check for mistakes.
container_name: desafio-frontend
restart: always
environment:
NEXT_PUBLIC_API_URL: http://localhost:3001
Copy link

Copilot AI Feb 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Docker networking configuration issue: The NEXT_PUBLIC_API_URL is set to 'http://localhost:3001', which will not work correctly when the frontend runs in a Docker container. The frontend needs to make API calls from the client browser, not from within the container. However, if users access the app via localhost:3000, then localhost:3001 from their browser is correct. But the variable name suggests this is for server-side calls. If this is meant for client-side calls from the browser, ensure users access both frontend and backend via localhost. If using a reverse proxy or different setup, this URL needs adjustment.

Copilot uses AI. Check for mistakes.
Comment on lines +107 to +120
if (
!data.consumption_history ||
data.consumption_history.length < 1
) {
throw new HttpException(
'PDF deve conter histórico de consumo',
HttpStatus.BAD_REQUEST,
);
}

// Limitar a 12 meses mais recentes se vier mais
if (data.consumption_history.length > 12) {
data.consumption_history = data.consumption_history.slice(0, 12);
}
Copy link

Copilot AI Feb 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Business rule inconsistency: The code only slices consumption_history to 12 months if there are MORE than 12, but doesn't handle the case where there are FEWER than 12 months. According to the business rules, each unidade must have exactly 12 months of history. If a PDF has fewer than 12 months, this should either be rejected with a clear error message, or the validation should be adjusted to accept a minimum number of months. Currently, PDFs with less than 12 months will pass the magic-pdf validation but fail later in the Unidade entity validation with a less clear error message.

Copilot uses AI. Check for mistakes.
) {}

@Post()
@UseInterceptors(FilesInterceptor('contasDeEnergia', 10))
Copy link

Copilot AI Feb 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing file upload security validations: The FilesInterceptor configuration lacks important security constraints. Add file size limits (e.g., 10MB per file as mentioned in the frontend), file type validation (ensure only PDFs with mimetype 'application/pdf' are accepted), and total payload size limit. Without these, the endpoint is vulnerable to DoS attacks through large file uploads. Example: @UseInterceptors(FilesInterceptor('contasDeEnergia', 10, { limits: { fileSize: 10 * 1024 * 1024 }, fileFilter: (req, file, cb) => { cb(null, file.mimetype === 'application/pdf') } }))

Copilot uses AI. Check for mistakes.
Comment on lines +29 to +93
@Post()
@UseInterceptors(FilesInterceptor('contasDeEnergia', 10))
async create(
@Body() body: CreateLeadDto,
@UploadedFiles() files: Array<Express.Multer.File>,
) {
try {
if (!files || files.length === 0) {
throw new HttpException(
'Pelo menos uma conta de energia deve ser enviada',
HttpStatus.BAD_REQUEST,
);
}

// Processar PDFs em paralelo
const processedPdfs = await Promise.all(
files.map(async (file) => {
const pdfData = await this.magicPdfService.decodePdf(
file.buffer,
file.originalname,
);

return {
codigoDaUnidadeConsumidora: pdfData.unit_key,
modeloFasico: this.magicPdfService.mapToModeloFasico(
pdfData.phaseModel,
),
enquadramento: this.magicPdfService.mapToEnquadramento(
pdfData.chargingModel,
),
mesDeReferencia: new Date(),
consumoEmReais: 0,
historicoDeConsumoEmKWH: pdfData.consumption_history.map(
(item) => ({
consumoForaPontaEmKWH: item.consumo_fp,
mesDoConsumo: new Date(item.consumo_date),
}),
),
};
}),
);

const result = await this.criarLeadUseCase.execute({
nomeCompleto: body.nomeCompleto,
email: body.email,
telefone: body.telefone,
informacoesDaFatura: processedPdfs,
});

return {
statusCode: HttpStatus.CREATED,
message: 'Lead criado com sucesso',
data: result,
};
} catch (error: any) {
if (error instanceof HttpException) {
throw error;
}

throw new HttpException(
error.message || 'Erro ao criar lead',
HttpStatus.BAD_REQUEST,
);
}
}
Copy link

Copilot AI Feb 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing rate limiting: The API endpoints, especially the POST /leads endpoint that processes PDFs via an external API, lack rate limiting protection. Without rate limiting, the API is vulnerable to abuse and DoS attacks. Consider adding NestJS throttler (@nestjs/throttler) to limit the number of requests per IP address, particularly for resource-intensive operations like PDF processing.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants