fix: remove unused imports and fix eslint errors#200
fix: remove unused imports and fix eslint errors#200LeidejanedaRosa wants to merge 33 commits intoSouJunior:mainfrom
Conversation
WalkthroughAlterações abrangentes em múltiplos módulos NestJS: ajustes de controladores/serviços do app, revisões em entidades TypeORM (incluindo RelationId e unicidade de e-mail), migrações (uuid-ossp e enums corrigidos), endpoints e fluxos em SavedJobs (novo DELETE), Curriculum, Jobs e Users, melhorias de repositórios com NotFoundException, validações/DTOs, e atualizações de Swagger. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor U as User (JWT)
participant C as SavedJobsController
participant S as DeleteSavedJobsService
participant R as SavedJobsRepository
U->>C: DELETE /saved-jobs/:id (id UUID v4)
note right of C: Validação com ParseUUIDPipe
C->>S: execute({ id }, user.id)
S->>R: deleteById({ id, userId })
alt Sucesso
R-->>S: { affected: 1 }
S-->>C: { status: 200, data: { message: 'Deleted' } }
C-->>U: 200 OK
else Não encontrado
R-->>S: { affected: 0 }
S-->>C: NotFoundException
C-->>U: 404 Not Found
end
sequenceDiagram
autonumber
actor U as User
participant UC as UserController
participant S as FindOneUserService
U->>UC: GET /users/:id
UC->>UC: if (req.user.id !== id && !isAdmin) throw Forbidden
alt Permitido
UC->>S: execute(id)
S-->>UC: user
UC-->>U: 200 OK
else Negado
UC-->>U: 403 Forbidden
end
sequenceDiagram
autonumber
actor Co as Company
participant J as JobsController
participant G as GetAllJobsFromLoggedCompanyService
participant R as JobRepository
Co->>J: GET /jobs/my
J->>G: execute(company.id)
G->>R: getAllJobsByCompanyId(company.id, status=ACTIVE)
alt Sem vagas
R-->>G: []
G-->>J: { status: 200, content: [] }
J-->>Co: 200 OK (lista vazia)
else Com vagas
R-->>G: [jobs]
G-->>J: { status: 200, content: [jobs] }
J-->>Co: 200 OK
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60–90 minutes Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
✨ Finishing touches
🧪 Generate unit tests
📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
Tip 👮 Agentic pre-merge checks are now available in preview!Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.
Please see the documentation for more information. Example: reviews:
pre_merge_checks:
custom_checks:
- name: "Undocumented Breaking Changes"
mode: "warning"
instructions: |
Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).Please share your feedback with us on this Discord post. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 22
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (14)
src/database/data-source.ts (2)
22-26: Ajustar globs de entities/migrations em src/database/data-source.ts para suportar CLI em TS
Atualmente só apontam paradist/**/*.js, impedindo o CLI (ts-node) de carregar entidades e migrações em dev; altere as propriedadesentitiesemigrationspara alternar entresrc/**/*.ts(desenvolvimento/CLI) edist/**/*.js(produção).
27-33: Forçar validação de certificado TLS em produção- ssl: - NODE_ENV == 'production' - ? { - ca: CA_CERT, - rejectUnauthorized: false, - } - : undefined, + ssl: + NODE_ENV === 'production' + ? { + ca: CA_CERT, // opcional: manter se houver CA customizado + rejectUnauthorized: true, + } + : undefined,
- Confirmar se
CA_CERTno ambiente de produção está em formato PEM (inclui-----BEGIN CERTIFICATE-----). Se for base64, converta antes comBuffer.from(CA_CERT, 'base64').src/modules/applications/applications.controller.ts (1)
16-21: Uso incorreto de@Query(): parâmetros nunca serão strings.
@Query()sem chave injeta o objeto de query inteiro. Ajuste para extrair as chaves específicas.- async saveApplication( - @Query() jobId: string, - @Query() curriculumId: string, - @LoggedUser() user: UsersEntity, - ) { + async saveApplication( + @Query('jobId') jobId: string, + @Query('curriculumId') curriculumId: string, + @LoggedUser() user: UsersEntity, + ) { return this.applicationsService.saveApplication(user, jobId, curriculumId); }src/app.module.ts (1)
31-59: Remover registro duplicado de SavedJobsService em AppModule
SavedJobsService já é provido e exportado em SavedJobsModule, então deve ser removido de AppModule.providers. Não há duplicidade para SavedJobsController, pois ele não está declarado em SavedJobsModule (caso queira, mova-o opcionalmente para lá).- providers: [AppService, UserRepository, SavedJobsService], + providers: [AppService, UserRepository],src/modules/curriculum/curriculum.service.ts (1)
17-19: Atualizar chamada defindAllCurriculumpara não receber argumentos
Em src/modules/curriculum/curriculum.service.ts (linha 17), remova o parâmetrouser.idda chamadafindAllCurriculum(), pois a assinatura atual não aceita argumentos.src/modules/savedjobs/savedjobs.controller.ts (3)
67-81: Mass assignment: não confie emuserIdvindo do body.Derive o
userIddo token para evitar que um usuário salve vaga para outro usuário.-async saveJob(@Body() createSavedJobDto: CreateSavedJobDto): Promise<any> { +async saveJob( + @Body() createSavedJobDto: CreateSavedJobDto, + @LoggedUser() user: UsersEntity, +): Promise<any> { try { - const savedJob = await this.savedJobsService.saveJob(createSavedJobDto); + const savedJob = await this.savedJobsService.saveJob({ + ...createSavedJobDto, + userId: user.id, + }); return { message: 'Sua vaga foi salva com sucesso!', statusCode: HttpStatus.CREATED, - savedJob: savedJob, + savedJob, }; } catch (error) { throw new HttpException( `Erro ao salvar vaga: ${error.message || 'erro desconhecido'}`, HttpStatus.INTERNAL_SERVER_ERROR, ); } }
83-92: DELETE: valide:ide simplifique DTO de rota.Valide UUID no parâmetro e passe o DTO esperado pelo service.
-async deleteSavedJob( - @Param() deleteSavedJobDto: DeleteSavedJobDto, - @LoggedUser() user: UsersEntity, -) { - return this.deleteSavedJobsService.execute(deleteSavedJobDto, user.id); -} +async deleteSavedJob( + @Param('id', new ParseUUIDPipe()) id: string, + @LoggedUser() user: UsersEntity, +) { + return this.deleteSavedJobsService.execute({ id } as DeleteSavedJobDto, user.id); +}
119-127: GET: dois@Query()competindo; habilitetransformpara parse correto.Sem
transform, números permanecem strings. ApliqueValidationPipenos dois ou consolide em um único DTO composto.- @Query() pageOptionsDto: PageOptionsDto, - @Query() query: GetAllSavedJobsDto, + @Query(new ValidationPipe({ transform: true })) pageOptionsDto: PageOptionsDto, + @Query(new ValidationPipe({ transform: true })) query: GetAllSavedJobsDto,Opcional (melhor): criar
GetAllSavedJobsQueryDto extends PageOptionsDtoe usar apenas um@Query().src/modules/company/repository/company-repository.ts (3)
112-119:updateRecoveryPassword: tipar parâmetros e tratar not found.-async updateRecoveryPassword( - id, - recoverPasswordToken, -): Promise<CompaniesEntity> { +async updateRecoveryPassword( + id: string, + recoverPasswordToken: string, +): Promise<CompaniesEntity> { const company = await this.companyRepository .findOneBy({ id }) .catch(handleError); + if (!company) { + throw new NotFoundException('Company not found'); + }
133-143:activateCompany: tratar not found e padronizar tratamento de erro no update.async activateCompany(id: string) { - const company = await this.companyRepository + const company = await this.companyRepository .findOneBy({ id }) .catch(handleError); + if (!company) { + throw new NotFoundException('Company not found'); + } company.mailConfirm = true; - await this.companyRepository.update(id, company); + await this.companyRepository.update(id, company).catch(handleError); return company; }
166-170:deleteCompanyById: aguardar deleção e validaraffected(status de existência).Atualmente não aguarda a operação nem valida se algo foi removido.
async deleteCompanyById(id: string): Promise<object> { - this.companyRepository.delete(id).catch(handleError); - - return { message: 'Company deleted successfully' }; + const result = await this.companyRepository.delete(id).catch(handleError); + if (!result?.affected) { + throw new NotFoundException('Company not found'); + } + return { message: 'Company deleted successfully' }; }src/modules/jobs/jobs.controller.ts (2)
104-110: Bug: uso incorreto de@Param()fazjobIdreceber um objeto em vez da string do:idIsso quebra
deleteJobService.execute(jobId, ...)em tempo de execução.Aplique:
- async archivedJob( - @Param() - jobId: string, - @Body('content') content: string, - ) { + async archivedJob( + @Param('id') jobId: string, + @Body('content') content: string, + ) { return this.deleteJobService.execute(jobId, content); }
34-35: Corrigir duplicação de arquivo e imports
- Renomear
src/shared/Swagger/decorators/jobs/get-all-jobs-of-logged-company.swagger copy.tsparaget-all-jobs-of-logged-company.swagger.ts(remover o sufixo" copy").- Remover o import de
GetAllJobsOfLoggedCompanySwagger(linha 33 emjobs.controller.ts) e ajustar o import deGetAllJobsSwaggerpara usar o path sem" copy".src/modules/user/repository/user.repository.ts (1)
93-101: updateRecoveryPassword: falta verificar usuário inexistente e update com objeto completo é arriscado.Sem checar
user, acessaruser.recoverPasswordTokenpode lançar erro. Além disso, atualizar com o objeto completo pode sobrescrever campos indevidamente.async updateRecoveryPassword(id: string, recoverPasswordToken: string) { const user = await this.usersRepository .findOneBy({ id }) .catch(handleError); - user.recoverPasswordToken = recoverPasswordToken; - - await this.usersRepository.update(id, user).catch(handleError); - return user; + if (!user) { + throw new NotFoundException('User not found'); + } + await this.usersRepository + .update(id, { recoverPasswordToken }) + .catch(handleError); + return this.usersRepository.findOneBy({ id }).catch(handleError); }
🧹 Nitpick comments (45)
src/database/data-source.ts (1)
18-18: Porta pode virar NaN; especifique radix e valor padrão
parseInt(TYPEORM_PORT)pode resultar emNaNquando a env não estiver definida. Defina radix e um fallback seguro.- port: parseInt(TYPEORM_PORT), + port: parseInt(TYPEORM_PORT ?? '5432', 10),src/database/migrations/1753987121418-InitialSchema.ts (8)
23-33: Enum com provável typo: 'INTERMEDITE' em vez de 'INTERMEDIATE'Os quatro enums de idiomas usam o valor 'INTERMEDITE'. Se o código espera 'INTERMEDIATE', haverá inconsistência de dados/validação.
- Se NUNCA foi deployado: corrija agora os valores no enum.
- Se já está em produção: crie nova migration para renomear o valor, por exemplo:
ALTER TYPE "public"."tb_languages_writing_enum" RENAME VALUE 'INTERMEDITE' TO 'INTERMEDIATE';
(repita para reading/listening/speaking).
Posso gerar a migration de rename se quiser.
41-41: Nomes de colunas com typos e inconsistentes
"st_adress" (provável "st_address") em tb_personal_data.
"desativated_at" em tb_curriculum e tb_comments (mistura PT/EN; sugerido "deactivated_at" ou usar PT-BR consistente).
Se ainda é a migration inicial não aplicada, corrija os nomes aqui.
Caso já aplicada, proponho migration de RENAME COLUMN para normalizar.
Posso preparar o patch conforme a convenção preferida (snake_case PT-BR ou EN).Also applies to: 47-47, 50-50
59-60: FK de tb_saved_jobs(jobId) com ON DELETE NO ACTION apesar de coluna ser nullableAo excluir um job, registros salvos impedirão a exclusão. Em geral, para “salvos”, espera-se SET NULL (ou CASCADE).
Aplique este diff (se seguro alterar a migration inicial); caso contrário, envio nova migration para ajustar a FK:
- `ALTER TABLE "tb_saved_jobs" ADD CONSTRAINT "FK_9bbd9a1f3bb4942f0471816b111" FOREIGN KEY ("jobId") REFERENCES "tb_jobs"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`, + `ALTER TABLE "tb_saved_jobs" ADD CONSTRAINT "FK_9bbd9a1f3bb4942f0471816b111" FOREIGN KEY ("jobId") REFERENCES "tb_jobs"("id") ON DELETE SET NULL ON UPDATE NO ACTION`,Como melhoria adicional de integridade, considere índice/único composto (userId, jobId) para evitar duplicatas de “vaga salva”.
Also applies to: 128-129
53-57: Padrão de nomenclatura inconsistente (camelCase vs snake_case) em colunasEx.: tb_companies (companyName, mailConfirm, profileKey…), tb_jobs (createdAt/updatedAt) vs outras tabelas com created_at/updated_at.
- Adote naming strategy global do TypeORM (snake_case) para novos artefatos.
- Se possível, normalize colunas existentes via migrations de rename para reduzir atrito em queries e BI.
Posso propor um plano incremental de renomeações e migração de dados.Also applies to: 80-81, 95-96
56-57: Integridade em tb_companies: cnpj/email e tipo JSON
- Sugestão: UNIQUE(cnpj) e, se aplicável, UNIQUE(email) para evitar duplicatas.
- "otherSite" usa JSON; prefira JSONB para melhor performance e indexação.
Ajustes sugeridos (em nova migration):
- ALTER TABLE "tb_companies" ADD CONSTRAINT "UQ_tb_companies_cnpj" UNIQUE ("cnpj");
- ALTER TABLE "tb_companies" ALTER COLUMN "otherSite" TYPE jsonb USING "otherSite"::jsonb;
Posso enviar o script completo (up/down) se concordar.
80-81: Regra de negócio de salário: adicione CHECK para boundsEvita registros inválidos com salaryMin > salaryMax.
Inclua (em nova migration ou logo após o CREATE TABLE):
ALTER TABLE "tb_jobs" ADD CONSTRAINT "CHK_tb_jobs_salary_bounds" CHECK (salaryMin IS NULL OR salaryMax IS NULL OR salaryMin <= salaryMax);
119-123: Índices para FKs mais consultadasPostgres não cria índice automaticamente para FK; joins/filters frequentes em user_id, job_id, curriculum_id se beneficiam de índices.
Sugestões:
- CREATE INDEX ON "tb_comments"("user_id"), ON "tb_comments"("job_id");
- CREATE INDEX ON "tb_applications"("job_id"), ("user_id"), ("curriculum_id");
- CREATE INDEX ON "tb_candidacies"("user_id"), ("job_id");
- CREATE INDEX ON "tb_reports"("job_id"), ("user_id").
Podemos medir com pg_stat_statements e criar somente nos hot paths.Also applies to: 134-141, 143-147, 152-156
80-81: updated_at não é auto-atualizado no DBHá DEFAULT now(), mas nenhuma trigger para atualizar updated_at em UPDATE; depende da aplicação lembrar de setar.
Se quiser garantir no banco, sugiro trigger (plpgsql) ou usar updatedAt via TypeORM @UpdateDateColumn nas entities para manter consistência.
Posso escrever a migration da trigger ou revisar as entities para @UpdateDateColumn.Also applies to: 95-96, 98-101
src/modules/mails/mail.module.ts (1)
14-21: Tipar porta do SMTP como número e definirsecureautomaticamente
config.get('MAIL_PORT')provavelmente retorna string; o Nodemailer esperanumber. Aproveite para remover os comentários duplicados desecuree calcular com base na porta (465).Aplicar este diff:
transport: { host: config.get('MAIL_HOST'), - port: config.get('MAIL_PORT'), - // secure: true, - - // secure: false, + port: Number(config.get('MAIL_PORT')), + secure: Number(config.get('MAIL_PORT')) === 465, auth: { user: config.get('MAIL_USER'), pass: config.get('MAIL_PASSWORD'), },src/modules/savedjobs/dtos/create-savedJob-dto.ts (1)
11-11: EOF newline adicionado — OKSem impacto funcional.
Sugestão leve: padronizar o nome do arquivo para kebab-case consistente (ex.:
create-saved-job-dto.ts).src/modules/savedjobs/dtos/delete-saved-job-dto.ts (1)
7-7: EOF newline adicionado — OKSem impacto funcional. Opcional: adicionar
@ApiProperty()para melhorar a documentação no Swagger.src/shared/Swagger/decorators/app/classes/health-check-response.swagger.ts (1)
4-12: Padronize e tipifique os status (enum) no SwaggerOs exemplos divergem entre 'DOWN' e 'OK'. Sugiro padronizar e restringir os valores para evitar ambiguidade e melhorar o contrato da API.
Ajuste proposto:
- @ApiProperty({ - example: 'DOWN', - }) - databaseStatus: string; + @ApiProperty({ + enum: ['UP', 'DOWN'], + example: 'UP', + }) + databaseStatus: 'UP' | 'DOWN'; - @ApiProperty({ - example: 'OK', - }) - mailerStatus: string; + @ApiProperty({ + enum: ['UP', 'DOWN'], + example: 'UP', + }) + mailerStatus: 'UP' | 'DOWN';Confirme quais valores o serviço realmente retorna hoje (ex.: 'UP'/'DOWN' vs 'OK'/'DOWN') e aplique a mesma convenção aos dois campos.
src/modules/user/dtos/create-user.dto.ts (2)
51-53: Mensagem de validação em inglês; padronize idiomaPadronize para pt-BR como o restante.
- message: 'The password does not match with the password confirmation', + message: 'A senha não corresponde à confirmação',
56-58: Campo opcional documentado como obrigatório no SwaggerUse
ApiPropertyOptionalpara refletir@IsOptional().- @IsOptional() - @IsEnum(UserRole) - type: UserRole; + @IsOptional() + @IsEnum(UserRole) + @ApiProperty({ required: false }) + type: UserRole;Opcionalmente troque para
ApiPropertyOptional()se preferir a semântica explícita do decorator.src/modules/savedjobs/dtos/get-all-savedjobs.dto.ts (1)
1-2: Campos opcionais: use ApiPropertyOptionalAmbos
userIdejobIdsão opcionais; documente como opcionais para não confundir clientes.-import { ApiProperty } from '@nestjs/swagger'; +import { ApiPropertyOptional } from '@nestjs/swagger'; ... - @ApiProperty({ + @ApiPropertyOptional({ required: false, description: 'ID do usuário que salvou a vaga', example: 'e2c1a2b7-8b4f-4c1b-bf94-2cb20de984d0', }) ... - @ApiProperty({ + @ApiPropertyOptional({ required: false, description: 'ID da vaga salva', - example: 'job_abc123', + example: '9f1a2b3c-4d5e-6f70-8a91-2b3c4d5e6f70', })Also applies to: 5-12, 14-21
src/modules/reports/repository/reports.repository.ts (3)
50-55: Deleção sem checaraffected; padronize resposta 404 quando não encontrado.Verifique
DeleteResult.affectede lanceNotFoundExceptioncaso 0.- async deleteReportById(id: string): Promise<object> { - await this.reportsRepository.delete(id).catch(handleError); - - return { - message: 'Deleted Report successfully', - }; - } + async deleteReportById(id: string): Promise<{ message: string }> { + const res = await this.reportsRepository.delete(id).catch(handleError); + if (!res.affected) { + throw new NotFoundException('Report not found'); + } + return { message: 'Deleted report successfully' }; + }Import já sugerido acima cobre
NotFoundException.
53-55: Consistência de idioma na resposta.O projeto usa mensagens em pt-BR em vários pontos; considere “Relatório excluído com sucesso”.
29-31: Typo no nome do método.
findAllRepots→findAllReports. Evita confusões em chamadas e auto-complete.Posso abrir um PR follow-up renomeando e ajustando referências?
src/modules/applications/applications.controller.ts (1)
15-15: Avaliar trocar GET por POST para operação de escrita.Salvar candidatura via GET pode causar cache/acidental replay; ideal usar
@Post()com DTO validado.src/database/entities/users.entity.ts (1)
42-47: Alinharenumà definiçãoRolesEnumpara type-safety.Evita divergência entre array literal e enum.
@Column({ type: 'enum', - enum: ['ADMIN', 'USER'], + enum: RolesEnum, default: RolesEnum.USER, }) type: string;src/modules/company/services/update-password-by-email.service.ts (2)
29-33: Use comparação estrita ao validar senhas.Trocar
!=por!==evita coerção implícita.- if (password != confirmPassword) { + if (password !== confirmPassword) { return { status: 400, data: { message: 'As senhas não conferem!' }, }; }
37-47: Confirme invalidação do token e robustez do envio de e-mail.
- Após redefinir a senha, o token deve ser invalidado/expirado para impedir reuso.
- Falhas em
sendCompanyConfirmationdevem ser tratadas para não mascarar uma redefinição bem-sucedida.Posso ajudar com um try/catch e log não-bloqueante caso o envio falhe, além de ajustar o repositório para limpar
recoverPasswordTokenno update. Quer que eu proponha o patch?src/modules/company/services/create-company.service.ts (1)
18-23: Paralelizar consultas de e-mail para reduzir latência.As duas buscas independentes podem rodar em paralelo com Promise.all, mantendo o comportamento e melhorando tempo de resposta.
Aplique este diff:
- const emailAlreadyInUseCompany = - await this.companyRepository.findOneByEmail(email); - - const emailAlreadyInUseUser = - await this.userRepository.findOneByEmail(email); + const [emailAlreadyInUseCompany, emailAlreadyInUseUser] = await Promise.all([ + this.companyRepository.findOneByEmail(email), + this.userRepository.findOneByEmail(email), + ]);src/database/entities/curriculum.entity.ts (1)
22-24: Semântica inalterada; apenas formatação — OK.Troca de aspas e espaço no objeto de opções não altera o comportamento do relacionamento. Mantém onDelete: 'CASCADE' e @joincolumn('user_id') coerentes.
Se consultas por usuário forem frequentes, considerar um índice em user_id para ajudar o planner:
+import { Index } from 'typeorm'; ... - @Column() + @Index() + @Column() user_id: string;src/app.controller.ts (1)
27-29: Evitar uso de @res direto para preservar pipes/interceptors.Como só repassa { status, data }, dá para retornar o objeto e deixar o Nest cuidar do status/serialização, facilitando testes e Swagger.
- async getHealthCheck(@Res() res: Response) { - const { status, data } = await this.appService.getHealthCheck(); - return res.status(status).send(data); - } + async getHealthCheck() { + return this.appService.getHealthCheck(); + }src/modules/company/company.controller.ts (1)
66-72: Nit: padronizar nome do parâmetrocreatecompanypara camelCaseMelhora a consistência com o DTO
CreateCompanyDto.Aplicar:
- async createCompany( - @Body() createcompany: CreateCompanyDto, + async createCompany( + @Body() createCompany: CreateCompanyDto, @Res() res: Response, ) { - const { data, status } = - await this.createCompanyService.execute(createcompany); + const { data, status } = + await this.createCompanyService.execute(createCompany);src/modules/savedjobs/services/savedjobs-cleaner.service.ts (1)
16-24: Cron: timezone e log resiliente; incluir vencidos “no instante”
- Se o servidor mudar de fuso, a limpeza à meia-noite pode não seguir o horário esperado; sugiro explicitar
timeZone.result.affectedpode virundefined; logue0por padrão.- Opcional: usar
LessThanOrEqual(now)para não perder itens que expiram exatamente no “agora”.Proposta:
- @Cron(CronExpression.EVERY_DAY_AT_MIDNIGHT) + @Cron(CronExpression.EVERY_DAY_AT_MIDNIGHT, { timeZone: process.env.TZ || 'UTC' }) async cleanExpiredJobs() { const now = new Date(); - const result = await this.savedJobsRepository.delete({ - expiresAt: LessThan(now), + const result = await this.savedJobsRepository.delete({ + expiresAt: LessThanOrEqual(now), }); - this.logger.log(`🧹 Removidos ${result.affected} registros expirados.`); + const affected = result.affected ?? 0; + this.logger.log(`🧹 Removidos ${affected} registros expirados.`); }src/modules/user/services/create-user.service.ts (1)
23-27: Cheque de e-mail em paralelo para reduzir latência.As duas consultas ao repositório podem ser feitas com Promise.all para economizar uma ida ao banco.
Aplique este diff:
- const emailAlreadyInUseCompany = - await this.companyRepository.findOneByEmail(email); - - const emailAlreadyInUseUser = - await this.userRepository.findOneByEmail(email); + const [emailAlreadyInUseCompany, emailAlreadyInUseUser] = await Promise.all([ + this.companyRepository.findOneByEmail(email), + this.userRepository.findOneByEmail(email), + ]);src/shared/interfaces/interfaces.ts (1)
1-3: Considere tipar a resposta com genérico para consistência.Facilita padronizar payloads dos serviços/controladores.
-export interface IGlobalResponse { - status: number; -} +export interface IGlobalResponse<T = unknown> { + status: number; + data?: T; + message?: string; +}src/modules/user/services/delete-user.service.ts (1)
6-6: Imutabilidade de dependência com readonly.Marca o repositório como readonly para evitar reatribuição acidental.
- constructor(private userRepository: UserRepository) {} + constructor(private readonly userRepository: UserRepository) {}src/modules/savedjobs/services/find-all-savedjobs.service.ts (1)
15-24: Evite capturar e reempacotar exceções indiscriminadamente.Hoje toda exceção é convertida em 500, perdendo contexto (p.ex., HttpException já tratável). Reaproveite HttpException ou remova o try/catch e deixe propagar.
Opção A — preservar HttpException e envolver demais erros:
- try { - return await this.savedJobsRepository.getAllSavedJobs( - pageOptionsDto, - filters, - ); - } catch (error) { - throw new InternalServerErrorException( - `Erro ao buscar vagas salvas: ${error.message}`, - ); - } + try { + return await this.savedJobsRepository.getAllSavedJobs( + pageOptionsDto, + filters, + ); + } catch (error) { + if (error instanceof InternalServerErrorException) throw error; + throw new InternalServerErrorException( + `Erro ao buscar vagas salvas: ${error?.message ?? 'erro desconhecido'}`, + ); + }Opção B — remover o try/catch (e o import de InternalServerErrorException):
- async getAllSavedJobs( + async getAllSavedJobs( pageOptionsDto: PageOptionsDto, filters: GetAllSavedJobsDto, ): Promise<PageDto<SavedJobsEntity>> { - try { - return await this.savedJobsRepository.getAllSavedJobs( - pageOptionsDto, - filters, - ); - } catch (error) { - throw new InternalServerErrorException( - `Erro ao buscar vagas salvas: ${error.message}`, - ); - } + return this.savedJobsRepository.getAllSavedJobs(pageOptionsDto, filters); }src/shared/Swagger/conflict.swagger.ts (1)
1-6: Adicionar metadata ao Swagger para melhorar a documentaçãoSugestão de incluir description e example no
@ApiProperty()para tornar a resposta mais clara na UI do Swagger.export class ConflictSwagger { - @ApiProperty() + @ApiProperty({ + description: 'Mensagem descrevendo o motivo do conflito (HTTP 409).', + example: 'Esta vaga já foi salva anteriormente pelo usuário.', + }) message: string; }src/modules/curriculum/interfaces/interfaces.ts (1)
1-11: Exportar ICurriculumsResponseContent
Nenhuma referência externa foi encontrada; exporte este tipo apenas se for reutilizado por outros módulos (p. ex., testes ou validações).src/shared/Swagger/decorators/savedjobs/create-savedjobs.swagger.decorator.ts (1)
1-39: Padronizar decorators de resposta específicos do SwaggerOpcionalmente, usar
ApiCreatedResponse,ApiBadRequestResponse,ApiConflictResponseeApiNotFoundResponsedeixa o código mais idiomático e legível do que oApiResponsegenérico.Exemplo de ajuste:
return applyDecorators( ApiBearerAuth(), ApiOperation({ summary: 'Salvar uma vaga', description: '...' }), ApiCreatedResponse({ description: 'Vaga salva com sucesso...', type: SavedJobsEntity }), ApiBadRequestResponse({ description: 'Requisição inválida...', type: BadRequestSwagger }), ApiConflictResponse({ description: 'Esta vaga já foi salva...', type: ConflictSwagger }), ApiNotFoundResponse({ description: 'Usuário ou vaga não encontrados.', type: NotFoundSwagger }), );src/modules/company/interfaces/interfaces.ts (1)
5-6: Padronize o shape dedata.content(evitar union para simplificar consumo).O union
CompaniesEntity | CompaniesEntity[]força checagens/guards em consumidores. Sugestões:
- Sempre array (preferível) ou
- Resposta genérica compartilhada em
shared.Exemplo (sempre array):
- content?: CompaniesEntity | CompaniesEntity[]; + content?: CompaniesEntity[];src/modules/jobs/services/get-all-jobs-from-logged-company.service.ts (1)
8-12: Remover injeção não utilizada (CompanyRepository).Evite dependências mortas e overhead de DI.
-import { CompanyRepository } from 'src/modules/company/repository/company-repository'; @@ export class GetAllJobsFromLoggedCompanyService { constructor( - private companyRepository: CompanyRepository, private jobsRepository: JobRepository, ) {}Se preferir, posso rodar um script para checar se
CompanyRepositoryé usado indiretamente aqui antes de remover.src/modules/jobs/services/delete-job.service.ts (2)
22-26: Tornar a operação idempotente quando o job já está arquivadoEvita update desnecessário e facilita reexecuções seguras.
Aplicar:
jobExists.status = StatusEnum.ARCHIVED; jobExists.content = content; - await this.jobRepository.updateJob(jobId, jobExists); + if (jobExists.status === StatusEnum.ARCHIVED) { + return { + status: 200, + data: { message: 'Job already archived' }, + }; + } + + await this.jobRepository.updateJob(jobId, jobExists);
25-32: Tratar falha do repositório para não vazar erro 500 não estruturadoHoje qualquer exceção do repositório estoura para cima. Sugiro capturar e responder com erro padronizado.
- await this.jobRepository.updateJob(jobId, jobExists); + try { + await this.jobRepository.updateJob(jobId, jobExists); + } catch { + return { + status: 500, + data: { message: 'Failed to archive job' }, + }; + }src/app.service.ts (2)
26-28: Health check deve responder 200 (OK), não 201 (Created)Status 201 pode confundir monitoramento e balanceadores.
return { - status: 201, + status: 200, data, };
49-63: Evitar envio real de e-mail no health checkEnvio real pode gerar ruído/custo. Prefira um
verify()/pingdo transporte ou um modo dry-run noMailService.Posso sugerir um
this.mailService.verify()que apenas testa a conexão sem enviar mensagem.src/modules/jobs/repository/job.repository.ts (1)
27-33: Acrescentar tratamento de erro e simplificar retornoManter consistência com o restante do repositório usando .catch(handleError) e retornando direto.
- async getAllJobsByCompanyId(companyId: string): Promise<JobsEntity[]> { - const jobs = await this.jobsRepository.find({ - where: { company_id: companyId }, - }); - - return jobs; - } + async getAllJobsByCompanyId(companyId: string): Promise<JobsEntity[]> { + return this.jobsRepository + .find({ where: { company_id: companyId } }) + .catch(handleError); + }src/modules/user/repository/user.repository.ts (3)
17-20: Marcar o repositório como readonly (imutável).Evita reatribuições acidentais e comunica a intenção.
- private usersRepository: Repository<UsersEntity>, + private readonly usersRepository: Repository<UsersEntity>,
76-79: Guarda de tipos e assinatura em updateMyPassword.Tipar
idajuda o compilador a flagrar chamadas incorretas.- async updateMyPassword(updateMyPasswordDto: UpdateMyPasswordDto, id) { + async updateMyPassword(updateMyPasswordDto: UpdateMyPasswordDto, id: string) {
84-89: Padronizar estilo: prefira async/await a then/catch aqui (opcional).Mantém o estilo do resto do arquivo e melhora legibilidade.
- return this.usersRepository - .update(id, updateMyPasswordDto) - .then(() => { - return this.usersRepository.findOneBy({ id }); - }) - .catch(handleError); + try { + await this.usersRepository.update(id, updateMyPasswordDto); + return await this.usersRepository.findOneBy({ id }); + } catch (error) { + return handleError(error); + }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (62)
src/app.controller.ts(1 hunks)src/app.module.ts(1 hunks)src/app.service.ts(1 hunks)src/database/data-source.ts(1 hunks)src/database/entities/applications.entity.ts(1 hunks)src/database/entities/comments.entity.ts(1 hunks)src/database/entities/courses.entity.ts(0 hunks)src/database/entities/curriculum.entity.ts(1 hunks)src/database/entities/languages.entity.ts(0 hunks)src/database/entities/users.entity.ts(1 hunks)src/database/entities/work-experiences.entity.ts(0 hunks)src/database/migrations/1753987121418-InitialSchema.ts(1 hunks)src/modules/applications/applications.controller.ts(1 hunks)src/modules/applications/applications.module.ts(1 hunks)src/modules/applications/interfaces/interfaces.ts(1 hunks)src/modules/applications/repository/applications.repository.ts(1 hunks)src/modules/comment/comment.controller.ts(1 hunks)src/modules/comment/comment.module.ts(1 hunks)src/modules/comment/interfaces/interfaces.ts(1 hunks)src/modules/comment/repository/comment.repository.ts(1 hunks)src/modules/company/company.controller.ts(2 hunks)src/modules/company/company.module.ts(1 hunks)src/modules/company/interfaces/interfaces.ts(1 hunks)src/modules/company/repository/company-repository.ts(6 hunks)src/modules/company/services/create-company.service.ts(1 hunks)src/modules/company/services/update-password-by-email.service.ts(1 hunks)src/modules/curriculum/curriculum.controller.ts(1 hunks)src/modules/curriculum/curriculum.module.ts(1 hunks)src/modules/curriculum/curriculum.service.ts(1 hunks)src/modules/curriculum/interfaces/interfaces.ts(1 hunks)src/modules/curriculum/repository/curriculum-repository.ts(1 hunks)src/modules/jobs/interfaces/interfaces.ts(1 hunks)src/modules/jobs/jobs.controller.ts(2 hunks)src/modules/jobs/jobs.module.ts(1 hunks)src/modules/jobs/repository/job.repository.ts(3 hunks)src/modules/jobs/services/delete-job.service.ts(1 hunks)src/modules/jobs/services/get-all-jobs-from-logged-company.service.ts(1 hunks)src/modules/mails/mail.module.ts(1 hunks)src/modules/reports/reports.controller.ts(1 hunks)src/modules/reports/reports.module.ts(1 hunks)src/modules/reports/repository/reports.repository.ts(1 hunks)src/modules/savedjobs/dtos/create-savedJob-dto.ts(1 hunks)src/modules/savedjobs/dtos/delete-saved-job-dto.ts(1 hunks)src/modules/savedjobs/dtos/get-all-savedjobs.dto.ts(1 hunks)src/modules/savedjobs/repository/savedjobs.repository.ts(1 hunks)src/modules/savedjobs/savedjobs.controller.ts(5 hunks)src/modules/savedjobs/services/find-all-savedjobs.service.ts(1 hunks)src/modules/savedjobs/services/savedjobs-cleaner.service.ts(1 hunks)src/modules/user/dtos/create-user.dto.ts(1 hunks)src/modules/user/interfaces/interfaces.ts(1 hunks)src/modules/user/repository/user.repository.ts(4 hunks)src/modules/user/services/create-user.service.ts(1 hunks)src/modules/user/services/delete-user.service.ts(1 hunks)src/modules/user/user.controller.ts(3 hunks)src/modules/user/user.module.ts(1 hunks)src/shared/Swagger/conflict.swagger.ts(1 hunks)src/shared/Swagger/decorators/app/classes/health-check-response.swagger.ts(1 hunks)src/shared/Swagger/decorators/jobs/get-all-jobs-of-logged-company.swagger copy.ts(1 hunks)src/shared/Swagger/decorators/jobs/get-all-jobs-of-logged-company.swagger.ts(1 hunks)src/shared/Swagger/decorators/savedjobs/create-savedjobs.swagger.decorator.ts(1 hunks)src/shared/Swagger/decorators/savedjobs/view-savedjobs.swagger.decorator.ts(1 hunks)src/shared/interfaces/interfaces.ts(1 hunks)
💤 Files with no reviewable changes (3)
- src/database/entities/work-experiences.entity.ts
- src/database/entities/courses.entity.ts
- src/database/entities/languages.entity.ts
🧰 Additional context used
🧬 Code graph analysis (20)
src/modules/savedjobs/services/find-all-savedjobs.service.ts (4)
src/modules/savedjobs/repository/savedjobs.repository.ts (1)
Injectable(8-46)src/shared/pagination/pageOptions.dto.ts (1)
PageOptionsDto(6-42)src/modules/savedjobs/dtos/get-all-savedjobs.dto.ts (1)
GetAllSavedJobsDto(4-22)src/shared/pagination/page.dto.ts (1)
PageDto(4-14)
src/modules/curriculum/interfaces/interfaces.ts (1)
src/shared/interfaces/interfaces.ts (1)
IGlobalResponse(1-3)
src/shared/Swagger/decorators/savedjobs/create-savedjobs.swagger.decorator.ts (3)
src/shared/Swagger/bad-request.swagger.ts (1)
BadRequestSwagger(2-5)src/shared/Swagger/conflict.swagger.ts (1)
ConflictSwagger(3-6)src/shared/Swagger/not-found.swagger.ts (1)
NotFoundSwagger(2-5)
src/app.service.ts (1)
src/shared/pagination/pageOptions.dto.ts (1)
PageOptionsDto(6-42)
src/modules/jobs/services/delete-job.service.ts (1)
src/modules/jobs/interfaces/interfaces.ts (1)
IJobsResponse(9-11)
src/modules/comment/repository/comment.repository.ts (3)
src/modules/comment/dtos/create-comment.dto.ts (1)
CreateCommentDto(4-29)src/shared/utils/handle-error.util.ts (1)
handleError(3-14)src/modules/comment/dtos/update-comment.dto.ts (1)
UpdateCommentDto(3-8)
src/modules/reports/repository/reports.repository.ts (4)
src/modules/reports/dtos/create-report.dto.ts (1)
CreateReportDto(4-29)src/shared/utils/handle-error.util.ts (1)
handleError(3-14)src/modules/reports/types/find-by-params.type.ts (1)
ReportParamsType(1-4)src/modules/reports/dtos/update-report.dto.ts (1)
UpdateReportDto(4-4)
src/modules/company/interfaces/interfaces.ts (1)
src/shared/interfaces/interfaces.ts (1)
IGlobalResponse(1-3)
src/modules/comment/interfaces/interfaces.ts (1)
src/shared/interfaces/interfaces.ts (1)
IGlobalResponse(1-3)
src/modules/jobs/repository/job.repository.ts (2)
src/modules/jobs/dtos/create-job.dto.ts (1)
CreateJobDto(19-199)src/shared/utils/handle-error.util.ts (1)
handleError(3-14)
src/modules/jobs/services/get-all-jobs-from-logged-company.service.ts (1)
src/modules/jobs/interfaces/interfaces.ts (1)
IJobsResponse(9-11)
src/modules/user/user.controller.ts (3)
src/modules/auth/decorator/logged-user.decorator.ts (1)
LoggedUser(7-18)src/modules/company/company.controller.ts (1)
updatePassword(127-137)src/modules/user/repository/user.repository.ts (1)
updatePassword(123-142)
src/modules/curriculum/repository/curriculum-repository.ts (2)
src/modules/curriculum/types/create-curriculum.type.ts (1)
CreateCurriculumType(3-7)src/shared/utils/handle-error.util.ts (1)
handleError(3-14)
src/modules/company/repository/company-repository.ts (2)
src/shared/utils/handle-error.util.ts (1)
handleError(3-14)src/modules/company/dtos/update-my-password.dto.ts (1)
UpdateMyPasswordDto(5-39)
src/shared/Swagger/decorators/savedjobs/view-savedjobs.swagger.decorator.ts (3)
src/shared/Swagger/decorators/user/classes/list-response.swagger.ts (1)
ListResponseSwagger(62-74)src/shared/Swagger/unauthorized.swagger.ts (1)
UnauthorizedSwagger(2-8)src/shared/Swagger/bad-request.swagger.ts (1)
BadRequestSwagger(2-5)
src/modules/jobs/interfaces/interfaces.ts (1)
src/shared/interfaces/interfaces.ts (1)
IGlobalResponse(1-3)
src/modules/user/interfaces/interfaces.ts (1)
src/shared/interfaces/interfaces.ts (1)
IGlobalResponse(1-3)
src/modules/savedjobs/savedjobs.controller.ts (1)
src/modules/savedjobs/dtos/create-savedJob-dto.ts (1)
CreateSavedJobDto(3-11)
src/modules/applications/interfaces/interfaces.ts (1)
src/shared/interfaces/interfaces.ts (1)
IGlobalResponse(1-3)
src/modules/user/repository/user.repository.ts (1)
src/shared/utils/handle-error.util.ts (1)
handleError(3-14)
🔇 Additional comments (51)
src/database/data-source.ts (1)
1-1: LGTM no ajuste de import do dotenvApenas formatação; manter o import com efeito colateral no topo é adequado.
src/database/migrations/1753987121418-InitialSchema.ts (1)
1-4: LGTM quanto à estrutura geral da migration e simetria up/downFormatação ficou mais legível e a ordem de drop no down está consistente (tipos após tabelas, índices antes da tabela).
Also applies to: 162-262
src/modules/reports/reports.controller.ts (1)
10-10: Limpeza de import OK; confirmar intenção do@ApiExcludeController()Remoção do
ApiOperationnão utilizado está correta. Apenas confirme se o controller deve mesmo ficar oculto no Swagger (há vários decorators Swagger nos métodos que não aparecerão).src/shared/Swagger/decorators/jobs/get-all-jobs-of-logged-company.swagger.ts (1)
2-2: Remoção de import não utilizado — OK; ver se respostas padronizadas ainda estão sendo aplicadasSe a convenção do projeto era aplicar
ApiOkResponse/erros via este decorator composto, confirme se isso foi movido para outro lugar para não perder os contratos de resposta no Swagger.src/modules/savedjobs/dtos/get-all-savedjobs.dto.ts (1)
17-21: Inconsistência removida: DTO de resposta não valida UUID
OGetAllSavedJobsDtoé usado apenas para resposta e não possui@IsUUID(). Remova a sugestão de alinhar exemplo ou alterar validação, pois não há validação de UUID neste DTO.Likely an incorrect or invalid review comment.
src/modules/reports/repository/reports.repository.ts (1)
12-15: Formatação do construtor — OK.Mudança apenas estética; DI continua correta.
src/modules/jobs/jobs.module.ts (1)
38-39: Vírgula terminal no providers — OK.Apenas estilo; sem impacto em DI.
src/modules/applications/applications.controller.ts (1)
1-1: Remoção de import não utilizado — OK.src/database/entities/users.entity.ts (1)
71-72: Ajuste cosmético na relação — OK.Sem alterações funcionais.
src/modules/company/services/update-password-by-email.service.ts (1)
19-20: Quebra de linha no await — OK.Apenas formatação; sem impacto.
src/modules/applications/applications.module.ts (1)
9-9: Apenas formatação — sem impacto funcional.Alteração para array inline no imports é ok e consistente com o restante do PR.
src/database/entities/comments.entity.ts (2)
22-22: Troca de aspas não altera semântica.onDelete: 'CASCADE' permanece correto para manter integridade referencial em exclusões de usuário.
29-29: Idem acima: semântica preservada.onDelete em relação ao Job continua 'CASCADE'. Sem mudanças funcionais.
src/modules/company/company.module.ts (1)
28-28: Apenas ajuste de indentação.Sem impacto no container de injeção do Nest. Ok.
src/app.module.ts (1)
21-27: Mudança cosmética nas imports.Somente inclusão de ponto e vírgula; sem impacto funcional. Ok.
src/modules/curriculum/curriculum.module.ts (1)
18-23: Reformatação de providers — OK.Quebra em múltiplas linhas melhora diffs e leitura sem mudar DI.
src/modules/savedjobs/repository/savedjobs.repository.ts (1)
1-46: Ordenação validada:PageOptionsDto.orderjá utiliza@IsEnum(Order)e valor padrãoOrder.ASC, restringindo os valores a 'ASC' | 'DESC'.src/modules/comment/comment.controller.ts (2)
43-44: Formatação do objeto do Throttle OKA vírgula final não altera o comportamento e mantém o estilo consistente.
41-45: Confirme unidades e agressividade do rate limitO
ttl: 2000no Throttler costuma ser em milissegundos (≈2s). Comlimit: 30, isso permite até ~15 req/s por IP/usuário, o que pode ser alto para criação de comentários. Vale validar com produto/ops se esse é o objetivo.src/modules/company/company.controller.ts (1)
70-71: Quebra de linha nas desestruturações OKA mudança é apenas de formatação; sem impacto em controle de fluxo ou resposta HTTP.
Also applies to: 118-119
src/modules/reports/reports.module.ts (1)
19-19: Formatação do array emimportsOKSemântica inalterada; alinhado ao padrão aplicado no PR.
src/modules/savedjobs/services/savedjobs-cleaner.service.ts (1)
1-15: Formatação e imports OKSem mudanças funcionais aparentes; tudo consistente.
src/modules/curriculum/curriculum.service.ts (1)
56-57: Refatoração de formatação OKSomente reflow; sem alteração de lógica.
src/modules/user/services/create-user.service.ts (1)
23-27: Mudança de formatação OK; sem impacto funcional.src/shared/interfaces/interfaces.ts (1)
2-3: Formatação padronizada; tudo certo.src/modules/user/user.module.ts (1)
43-45: LGTM na limpeza/vírgula final em providers.src/modules/comment/comment.module.ts (1)
22-29: Mudanças de formatação: OKA normalização do array do TypeORM e vírgula de cauda no Throttler está consistente com o restante do PR. Sem impacto funcional aparente.
src/modules/comment/repository/comment.repository.ts (1)
21-29: Consulta comfindefindOneBy: OKUso de filtro por
desativated_at: nullem listagem e busca por ID evita retorno de registros desativados. Mantém o padrão do repositório.src/modules/company/interfaces/interfaces.ts (1)
10-11: LGTM — tipagem e estilo alinhados ao padrão do monorepo.src/modules/applications/repository/applications.repository.ts (1)
10-13: Apenas formatação — sem impacto funcional.src/modules/jobs/services/get-all-jobs-from-logged-company.service.ts (1)
19-22: Mensagem e contrato OK quando não há vagas.A checagem
if (!jobs)trata nulo/undefined e não conflita com[]. Mantém 200 para array vazio.src/modules/jobs/services/delete-job.service.ts (1)
10-12: LGTM na limpeza e padronizaçãoRemoção de import não utilizado e ajustes de formatação/
;estão corretos.src/modules/jobs/jobs.controller.ts (1)
80-86: LGTM na reformatação do handler com@Res()Formatação consistente e sem impacto funcional.
src/modules/applications/interfaces/interfaces.ts (1)
4-11: Padronização com;nas interfacesBoa limpeza e alinhamento ao estilo do projeto; sem mudanças semânticas.
src/modules/jobs/interfaces/interfaces.ts (1)
4-11: Padronização com;nas interfacesCoerente com o restante do PR e sem impacto funcional.
src/modules/curriculum/repository/curriculum-repository.ts (3)
10-13: Refatoração de construtor OKApenas formatação e DI permanecem corretos.
20-22: Encadeamento com catch(handleError) consistenteBoa padronização de tratamento de erro.
32-34: deleteByKey OKUso de softDelete com tratamento de erro está adequado.
src/database/entities/applications.entity.ts (1)
19-19: Padronização de aspas nas opções onDeleteAlteração somente estética, mantendo CASCADE.
Also applies to: 26-26, 33-33
src/modules/comment/interfaces/interfaces.ts (3)
1-2: Imports e estilo padronizadosNada funcional alterado.
5-6: Terminação com ponto e vírgula nas interfacesOk e consistente com o restante do PR.
10-11: Tipo de resposta padronizadoSem mudanças semânticas.
src/modules/jobs/repository/job.repository.ts (2)
17-20: Construtor formatado — sem impacto funcionalDI continua correta.
70-71: Apenas formatação no createQueryBuilderSem alteração lógica percebida.
src/modules/user/user.controller.ts (3)
104-104: Remoção de parâmetro não utilizadoOK para resolver lint. Verifique se a rota deve ser restrita a admin (SwaggerGetUserAdm sugere isso). Hoje só há AuthGuard(); se a intenção for admin-only, considere @LoggedAdmin ou guard de roles.
125-125: Apenas formatação no deleteUserSem impacto funcional.
146-147: Compactação da chamada — sem impacto funcionalOK.
src/modules/user/repository/user.repository.ts (3)
52-52: LGTM nas consultas diretas por id/email.Uso consistente de
findOneBy({...}).catch(handleError). Sem observações.Also applies to: 56-56
118-121: LGTM no findByToken.Consulta simples +
catch(handleError)consistente.
45-48: searchUserByName não é referenciado em lugar nenhum: não há chamadas asearchUserByName()na base de código, logo a mudança de contrato não causa regressão. Remova este método se estiver sem uso ou, quando implantá-lo, reintroduza filtro por nome (ILIKE), incluaidno select, ordenação e paginação.Likely an incorrect or invalid review comment.
src/modules/user/interfaces/interfaces.ts (1)
1-2: LGTM — normalização de ponto-e-vírgula e indentação.Ajustes apenas de formatação/terminadores; nenhum impacto em tempo de execução.
Also applies to: 5-6, 10-11
src/shared/Swagger/decorators/jobs/get-all-jobs-of-logged-company.swagger copy.ts
Outdated
Show resolved
Hide resolved
src/shared/Swagger/decorators/savedjobs/view-savedjobs.swagger.decorator.ts
Outdated
Show resolved
Hide resolved
src/shared/Swagger/decorators/savedjobs/view-savedjobs.swagger.decorator.ts
Show resolved
Hide resolved
Docstrings generation was requested by @MikaelMelo1. * #200 (comment) The following files were modified: * `src/shared/Swagger/decorators/jobs/get-all-jobs-of-logged-company.swagger copy.ts` * `src/shared/Swagger/decorators/jobs/get-all-jobs-of-logged-company.swagger.ts` * `src/shared/Swagger/decorators/savedjobs/create-savedjobs.swagger.decorator.ts` * `src/shared/Swagger/decorators/savedjobs/view-savedjobs.swagger.decorator.ts`
|
Note Generated docstrings for this pull request at #202 |
MikaelMelo1
left a comment
There was a problem hiding this comment.
Muito bom, só ajustar alguns detalhes.
src/shared/Swagger/decorators/jobs/get-all-jobs-of-logged-company.swagger.ts
Show resolved
Hide resolved
src/shared/Swagger/decorators/jobs/get-all-jobs-of-logged-company.swagger copy.ts
Outdated
Show resolved
Hide resolved
src/shared/Swagger/decorators/savedjobs/view-savedjobs.swagger.decorator.ts
Show resolved
Hide resolved
…Exception, log stack, generic 500)
…tity[] using ILike
…ule, keep only in SavedJobsModule
There was a problem hiding this comment.
Actionable comments posted: 21
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (9)
src/modules/jobs/repository/job.repository.ts (2)
45-47: Bug: uso de{}em andWhere quebra em runtime
andWherenão aceita objeto vazio; comparams.modalityfalsy isso deve lançar erro. Troque por umif.- .andWhere(params.modality ? 'jobs.modality = :modality' : {}, { - modality: params.modality, - }) + if (params.modality) { + queryBuilder.andWhere('jobs.modality = :modality', { + modality: params.modality, + }); + }
48-50: Potencial SQLi: validar/whitelist orderByColumn antes de usar
- Problema: pageOptionsDto.orderByColumn é string (src/shared/pagination/pageOptions.dto.ts) e é interpolada em .orderBy(
jobs.${pageOptionsDto.orderByColumn}, ...), permitindo injeção via coluna.- Correção: criar enum/whitelist de colunas permitidas e mapear para os identificadores seguros (ex.: 'jobs.id', 'jobs.title') antes de chamar .orderBy; confirmar que pageOptionsDto.order é o enum Order ('ASC'|'DESC') — já tipado.
- Arquivos afetados: src/modules/jobs/repository/job.repository.ts (≈46–50, 118–122), src/modules/user/repository/user.repository.ts (≈32), src/modules/company/repository/company-repository.ts (≈28–31).
src/modules/company/repository/company-repository.ts (3)
116-125: Falta tipagem e tratamento de “not found”Se a empresa não existir,
companyseráundefinedesavequebra. Tipar argumentos e validar antes.- async updateRecoveryPassword( - id, - recoverPasswordToken, - ): Promise<CompaniesEntity> { + async updateRecoveryPassword( + id: string, + recoverPasswordToken: string, + ): Promise<CompaniesEntity> { const company = await this.companyRepository .findOneBy({ id }) .catch(handleError); + if (!company) { + throw new NotFoundException('Empresa não encontrada'); + } + company.recoverPasswordToken = recoverPasswordToken; await this.companyRepository.save(company).catch(handleError); return company; }
133-143: Ativação sem checar existência e sem tratamento de erro no updateAdicione
not found, atualize apenas a coluna necessária e mantenha o catch.async activateCompany(id: string) { - const company = await this.companyRepository - .findOneBy({ id }) - .catch(handleError); - - company.mailConfirm = true; - - await this.companyRepository.update(id, company); - - return company; + const company = await this.companyRepository + .findOneBy({ id }) + .catch(handleError); + if (!company) { + throw new NotFoundException('Empresa não encontrada'); + } + await this.companyRepository + .update(id, { mailConfirm: true }) + .catch(handleError); + return this.companyRepository.findOneBy({ id }).catch(handleError); }
165-169: Não aguarda a remoção e não trata “not found”
deletenão é aguardado e o método sempre retorna sucesso. Valideaffected.- async deleteCompanyById(id: string): Promise<object> { - this.companyRepository.delete(id).catch(handleError); - - return { message: 'Company deleted successfully' }; - } + async deleteCompanyById(id: string): Promise<object> { + const result = await this.companyRepository.delete(id).catch(handleError); + if (!result?.affected) { + throw new NotFoundException('Empresa não encontrada'); + } + return { message: 'Empresa removida com sucesso' }; + }src/modules/user/services/create-user.service.ts (1)
29-36: Status incorreto para e-mail já cadastrado (404).“Já cadastrado” deve retornar 409 (Conflict), não 404 (Not Found). Além disso, seu catch já retorna 409 para violação de unique, então padronize.
- return { - status: 404, + return { + status: 409, data: { message: 'E-mail já cadastrado', }, };src/modules/user/repository/user.repository.ts (1)
31-35: Risco de SQL injection via orderBy; faça whitelisting de colunas.Interpolar
orderByColumndireto na string permite injeção se o valor não for estritamente validado. Faça whitelist explícita.Aplicar:
- queryBuilder - .orderBy(`users.${pageOptionsDto.orderByColumn}`, pageOptionsDto.order) + const allowedOrderColumns = new Set(['id', 'name', 'email', 'created_at']); + const orderByColumn = allowedOrderColumns.has(pageOptionsDto.orderByColumn) + ? pageOptionsDto.orderByColumn + : 'created_at'; + queryBuilder + .orderBy(`users.${orderByColumn}`, pageOptionsDto.order)src/modules/savedjobs/savedjobs.controller.ts (2)
73-87: POST vulnerável a IDOR: não aceiteuserIddo cliente; derive de@LoggedUser()Hoje o payload aceita
userIddiretamente do cliente, permitindo salvar vagas em nome de outro usuário autenticado. Derive ouserIda partir do token e ignore o corpo.- async saveJob(@Body() createSavedJobDto: CreateSavedJobDto): Promise<any> { + async saveJob( + @Body() createSavedJobDto: CreateSavedJobDto, + @LoggedUser() user: UsersEntity, + ): Promise<any> { try { - const savedJob = await this.savedJobsService.saveJob(createSavedJobDto); + const savedJob = await this.savedJobsService.saveJob({ + ...createSavedJobDto, + userId: user.id, // força o vínculo ao usuário logado + }); return { message: 'Sua vaga foi salva com sucesso!', statusCode: HttpStatus.CREATED, savedJob: savedJob, };Sugestão complementar (fora deste arquivo): remova
userIddoCreateSavedJobDtopara evitar a exigência do campo no corpo:// src/modules/savedjobs/dtos/create-savedJob-dto.ts export class CreateSavedJobDto { @IsUUID() @IsNotEmpty() jobId: string; }
133-141: Bloquear vazamento: filtrar por usuário autenticado (crítico)GET /saved-jobs aceita userId via DTO e o repositório aplica filters.userId; o controller não força o userId do usuário logado — usuários autenticados podem listar vagas salvas de terceiros. Adicionar @loggeduser() ao método e forçar userId = user.id (ou passar { ...query, userId: user.id }) antes de chamar o serviço; se a rota for administrativa, proteger com guard de roles.
- async getAllSavedJobs( - @Query() pageOptionsDto: PageOptionsDto, - @Query() query: GetAllSavedJobsDto, - ) { + async getAllSavedJobs( + @Query() pageOptionsDto: PageOptionsDto, + @Query() query: GetAllSavedJobsDto, + @LoggedUser() user: UsersEntity, + ) { @@ - const savedJobs = await this.findAllSavedJobsService.getAllSavedJobs( - pageOptionsDto, - query, - ); + const savedJobs = await this.findAllSavedJobsService.getAllSavedJobs( + pageOptionsDto, + { ...query, userId: user.id }, + );Arquivos/locais relevantes: src/modules/savedjobs/savedjobs.controller.ts (getAllSavedJobs — linhas ~133–141), src/modules/savedjobs/repository/savedjobs.repository.ts (aplica filters.userId — linhas ~28–30), src/modules/savedjobs/dtos/get-all-savedjobs.dto.ts (userId opcional).
♻️ Duplicate comments (7)
src/modules/curriculum/curriculum.controller.ts (1)
30-32: Unificar padrão de resposta: remover@Resdo upload e retornar diretamente (igual ao GET)Este ponto já foi levantado antes; manter consistente melhora DX e documentação Swagger. Ajuste o método
uploadCurriculumpara retornar o objeto do service e remova os imports deRes/Response.// Sugestão (fora do hunk): padronizar upload @UploadCurriculumSwagger() @Post('upload') @UseInterceptors(FileInterceptor('file')) async uploadCurriculum( @LoggedUser() user: UsersEntity, @UploadedFile() file: Express.Multer.File, ) { return this.curriculumService.uploadCurriculum(file, user); }src/shared/Swagger/decorators/jobs/get-all-jobs-of-logged-company.swagger.ts (1)
2-9: Completar documentação Swagger com respostas 200/500Reforço a sugestão anterior de adicionar @ApiOkResponse e @ApiInternalServerError para melhorar a doc.
-import { ApiOperation } from '@nestjs/swagger'; +import { ApiOperation, ApiOkResponse, ApiInternalServerError } from '@nestjs/swagger'; export function GetAllJobsSwagger() { return applyDecorators( ApiOperation({ summary: 'Buscar todas as vagas.', }), + ApiOkResponse({ description: 'Lista de vagas retornadas com sucesso.' }), + ApiInternalServerError({ description: 'Erro interno do servidor.' }), ); }src/modules/jobs/repository/job.repository.ts (2)
120-122: Repetição do risco de SQLi no orderByMesma observação do outro método: garanta whitelist de colunas e direção.
98-105: Mensagens de erro: padronizar idiomaNo serviço as mensagens estão em inglês, aqui em PT-BR. Padronize (ou use i18n) para evitar UX inconsistente.
- throw new NotFoundException('Vaga não encontrada ou inativa'); + throw new NotFoundException('Job not found or inactive');src/modules/company/repository/company-repository.ts (1)
145-163: Tiparide evitar duplo tratamento de erro (redundante e pode mascarar stack)Dentro do
try, você já captura erro; remova.catch(handleError)internos e tipaid.- async updatePassword(id, password: string): Promise<CompaniesEntity> { + async updatePassword(id: string, password: string): Promise<CompaniesEntity> { try { - const company = await this.companyRepository - .findOneBy({ id }) - .catch(handleError); + const company = await this.companyRepository.findOneBy({ id }); if (!company) { throw new NotFoundException('Empresa não encontrada'); } const data = { recoverPasswordToken: null, password, }; - await this.companyRepository.update(id, data); - return await this.companyRepository.findOneBy({ id }); + await this.companyRepository.update(id, data); + return await this.companyRepository.findOneBy({ id }); } catch (error) { - handleError(error); + handleError(error); } }src/modules/user/repository/user.repository.ts (2)
111-125: LGTM:activateUseragora trata 404 e evita mutação local.Fluxo alinhado ao que foi sugerido anteriormente.
133-147: LGTM:updatePasswordrobusto (404 + atualização granular).Boa prática limpar
recoverPasswordTokenjunto. Só garanta hashing prévio (comentário acima).
🧹 Nitpick comments (67)
src/modules/curriculum/curriculum.service.ts (2)
14-24: Padronizar contrato de resposta e corrigir nome da variável
- GET retorna
{ status, data }, enquanto o POST usa@Rese envia apenasdata. Padronize (ou remova o wrapperstatusdo service e deixe o controller definir o status, ou remova@Resno upload e retorne o mesmo wrapper).- Renomeie
curriculunspara algo claro (ex.:curricula).- async getAllCurriculum( - user: UsersEntity, - ): Promise<{ status: number; data: any }> { - const curriculuns = await this.curriculumRepository.findAllCurriculum( - user.id, - ); + async getAllCurriculum( + user: UsersEntity, + ): Promise<{ status: number; data: any }> { + const curricula = await this.curriculumRepository.findAllCurriculum( + user.id, + ); return { status: 200, - data: curriculuns, + data: curricula, }; }
55-57: Mesma correção de nomenclatura aquiRenomeie
curriculunsparacurriculum/curricula.- const curriculuns = - await this.curriculumRepository.saveCurriculum(saveNewCurriculum); + const curriculum = + await this.curriculumRepository.saveCurriculum(saveNewCurriculum);src/modules/curriculum/curriculum.controller.ts (1)
30-32: RemoverawaitdesnecessárioO retorno pode ser direto.
- async getAllCurriculum(@LoggedUser() user: UsersEntity) { - return await this.curriculumService.getAllCurriculum(user); - } + async getAllCurriculum(@LoggedUser() user: UsersEntity) { + return this.curriculumService.getAllCurriculum(user); + }src/modules/curriculum/repository/curriculum-repository.ts (2)
25-29: Filtro poruserIdcorrigido — bomConsulta agora está consistente com o service. Se a tabela crescer, garanta índice em
user_idna entidade/migração.
32-35: Checar resultado dosoftDeletepara sinalizar “não encontrado” (opcional)Opcionalmente diferencie exclusão idempotente de registro inexistente via
result.affected.// Exemplo (fora do hunk): const result = await this.curriculumRepository .softDelete({ fileKey: key }) .catch(handleError); if (!result.affected) { // throw new NotFoundException('Curriculum não encontrado'); // ou ignore para idempotência }src/database/entities/applications.entity.ts (2)
33-34: Troca de @column por @RelationId em user_id é mudança funcional. Verifique usos de escrita.
- Com
@RelationId,user_idvira derivado e não é persistível (atribuições comoapplication.user_id = '...'deixam de surtir efeito).- Sugiro renomear para
userIdpara evitar ambiguidade com o nome da coluna físicauser_ide alinhar com o padrão TypeORM.Proposta mínima:
- @RelationId((app: ApplicationEntity) => app.user) - user_id: string; + @RelationId((app: ApplicationEntity) => app.user) + userId: string;Se quiser padronizar (recomendado), exponha também os IDs de
jobecurriculumvia RelationId (sem mapear@Columnduplicado):@RelationId((app: ApplicationEntity) => app.job) jobId: string; @RelationId((app: ApplicationEntity) => app.curriculum) curriculumId: string;E então remova os
@Column()redundantes desses FKs para evitar metadados duplicados.
28-31: Confirmado — não é necessário migration extra (user_id já é NOT NULL na migration inicial)Verificado: src/database/migrations/1753987121418-InitialSchema.ts cria tb_applications com "user_id" uuid NOT NULL e adiciona a FK ON DELETE CASCADE — o entity com nullable: false está consistente com o schema.
- Opcional: se a regra de negócio for "uma application por usuário por vaga", adicione índice único composto (ex.: @Index('uq_applications_user_job', ['user','job'], { unique: true }) ou via migration).
src/app.controller.ts (3)
26-28: Preserve interceptors/filters usandopassthroughCom
@Res()puro, você desativa interceptors e filtros globais do Nest para esta rota. Para manter status dinâmico e ainda beneficiar da pipeline, use@Res({ passthrough: true })e retorne odata.- async getHealthCheck(@Res() res: Response) { - const { status, data } = await this.appService.getHealthCheck(); - return res.status(status).send(data); - } + async getHealthCheck(@Res({ passthrough: true }) res: Response) { + const { status, data } = await this.appService.getHealthCheck(); + res.status(status); + return data; + }Nit adicional: o decorator
@SwaggerHealthCheck()já aplicaApiOperation. A anotação logo acima pode ser removida para evitar metadados duplicados.
21-25: Swagger duplicado (nit)
@SwaggerHealthCheck()já defineApiOperationcom o mesmo summary. Remova a duplicação para evitar metadata conflitante no Swagger.@SwaggerHealthCheck() - @ApiOperation({ - summary: 'Retorna status dos serviços de email e banco de dados', - })
6-6: Evitar acoplamento ao Express — imports detectadosForam encontrados imports de Request/Response de 'express' nos arquivos abaixo:
- src/app.controller.ts
- src/modules/curriculum/curriculum.controller.ts
- src/modules/user/user.controller.ts
- src/modules/user/services/create-user.service.ts
- src/modules/company/company.controller.ts
- src/modules/jobs/jobs.controller.ts
- src/modules/auth/auth.controller.ts
Ação recomendada: se houver intenção de migrar para Fastify, remover usos diretos de tipos/objetos do 'express' e aplicar uma das abordagens:
src/modules/comment/repository/comment.repository.ts (6)
1-1: Padronize o uso de IsNull para filtros de “desativado = null”.Há mistura entre
desativated_at: nulleIsNull(). Sugiro padronizar comIsNull()em todos os pontos para evitar ambiguidades e manter consistência.- .find({ where: { desativated_at: null } }) + .find({ where: { desativated_at: IsNull() } }) - .findOneBy({ id, desativated_at: null }) + .findOneBy({ id, desativated_at: IsNull() })Also applies to: 22-24, 39-40
5-5: Caminho de import redundante.Remova o
./antes de..no import.-import { CreateCommentDto } from './../dtos/create-comment.dto'; +import { CreateCommentDto } from '../dtos/create-comment.dto';
7-8: Una imports de @nestjs/common.Evita duplicação de declaração.
-import { Injectable } from '@nestjs/common'; -import { NotFoundException } from '@nestjs/common'; +import { Injectable, NotFoundException } from '@nestjs/common';
38-41: Troque spread por merge do TypeORM para update seguro.Usar spread do entity pode carregar campos/relacionamentos indesejados. Prefira
merge+save.- return this.commentsRepository - .save({ - ...comment, - ...data, - }) - .catch(handleError); + const merged = this.commentsRepository.merge(comment, data); + return this.commentsRepository.save(merged).catch(handleError);Also applies to: 46-51
55-61: Evite “delete” idempotente falso: atualize apenas registros ativos.Hoje o
update(id, …)marca como desativado mesmo se já estiver desativado, retornando sucesso. Atualize com critério por ativo e mantenha o 404 quando nada for afetado.- const result = await this.commentsRepository - .update(id, { desativated_at: new Date() }) + const result = await this.commentsRepository + .update({ id, desativated_at: IsNull() }, { desativated_at: new Date() }) .catch(handleError); - if (!result || result.affected === 0) { - throw new NotFoundException('Comentário não encontrado para exclusão'); - } + if (!result || result.affected === 0) { + throw new NotFoundException('Comentário não encontrado ou já desativado'); + }
63-63: Consistência de idioma nas mensagens.O projeto mistura PT/EN. Padronize a resposta para PT-BR aqui.
- return { message: 'Comment deleted successfully' }; + return { message: 'Comentário desativado com sucesso' };src/modules/reports/services/find-all-reports.service.ts (1)
11-13: Ajuste de gramática/idioma na resposta vazia.Mensagem está em EN e com gramática estranha. Sugestões: “No reports” ou PT-BR.
- return { message: 'Reports is empty' }; + return { message: 'Nenhum relatório encontrado' };src/modules/reports/repository/reports.repository.ts (3)
8-9: Una imports de @nestjs/common.Reduza duplicação.
-import { Injectable } from '@nestjs/common'; -import { NotFoundException } from '@nestjs/common'; +import { Injectable, NotFoundException } from '@nestjs/common';
37-48: Defina tipo de retorno explícito em updateReport.Melhora a segurança de tipos e a DX.
- async updateReport(id: string, data: UpdateReportDto) { + async updateReport(id: string, data: UpdateReportDto): Promise<ReportsEntity> {
50-56: Chequeaffectedao deletar e padronize idioma.Evita sucesso silencioso quando nada é removido e uniformiza a mensagem.
- async deleteReportById(id: string): Promise<object> { - await this.reportsRepository.delete(id).catch(handleError); - - return { - message: 'Deleted Report successfully', - }; - } + async deleteReportById(id: string): Promise<object> { + const result = await this.reportsRepository.delete(id).catch(handleError); + if (!result || result.affected === 0) { + throw new NotFoundException('Report not found'); + } + return { message: 'Relatório deletado com sucesso' }; + }src/shared/Swagger/decorators/jobs/get-all-jobs-of-logged-company.swagger.ts (1)
4-10: Renomeio do decorator: alinhar nome do arquivo e manter compatibilidadeO arquivo ainda chama “...of-logged-company”, mas o decorator agora é genérico. Sugiro alinhar o nome do arquivo ou exportar um alias para evitar que consumidores antigos quebrem.
export function GetAllJobsSwagger() { return applyDecorators( ApiOperation({ summary: 'Buscar todas as vagas.', }), ); } + +// Alias para compatibilidade retroativa (opcional) +export const GetAllJobsOfLoggedCompanySwagger = GetAllJobsSwagger;src/modules/jobs/repository/job.repository.ts (3)
29-33: Padronizar tratamento de erro com handleErrorOutros métodos usam
.catch(handleError). Consistência ajuda na observabilidade.- const jobs = await this.jobsRepository.find({ - where: { company_id: companyId, status: StatusEnum.ACTIVE }, - }); + const jobs = await this.jobsRepository + .find({ + where: { company_id: companyId, status: StatusEnum.ACTIVE }, + }) + .catch(handleError);
63-63: Tratar possíveis erros em getRawAndEntitiesManter o padrão de
.catch(handleError).- const { entities } = await queryBuilder.getRawAndEntities(); + const { entities } = await queryBuilder.getRawAndEntities().catch(handleError);
70-70: Tipagem mais precisa do retornoEvite
any; retornePromise<JobsEntity | null>.- async findOneById(id: string): Promise<any> { + async findOneById(id: string): Promise<JobsEntity | null> {src/modules/jobs/services/get-all-jobs-from-logged-company.service.ts (2)
8-11: Injeção não utilizada: remover CompanyRepositoryEconomiza memória e alinha ao objetivo do PR (imports/injeções não usadas).
-import { CompanyRepository } from 'src/modules/company/repository/company-repository'; import { IJobsResponse } from '../interfaces/interfaces'; import { JobRepository } from '../repository/job.repository'; @@ constructor( - private companyRepository: CompanyRepository, - private jobsRepository: JobRepository, - ) {} + private jobsRepository: JobRepository, + ) {}
16-16: Simplificar checagem de vazioMenos verboso e seguro.
- if (!jobs || (Array.isArray(jobs) && jobs.length === 0)) { + if (!jobs?.length) {src/database/migrations/1699999999999-AddUniqueEmailToCompanies.ts (1)
6-10: Observabilidade/ops: risco de lock em tabela grandeALTER TABLE UNIQUE bloqueia escrita. Em bases grandes, considere estratégia com CREATE UNIQUE INDEX CONCURRENTLY + ADD CONSTRAINT USING INDEX, executada fora de transação. Planeje janela de manutenção.
src/database/entities/companies.entity.ts (1)
20-21: Opcional: unicidade case-insensitive para e-mailSe o requisito for tratar e-mails sem sensibilidade a maiúsculas/minúsculas, considere normalizar em lower() na aplicação e/ou usar CITEXT/índice único em lower(email).
Você quer que o sistema trate e-mails como case-insensitive? Posso propor a migração correspondente.
src/modules/company/repository/company-repository.ts (2)
47-56: Mensagem inconsistente e status para ID inválido
- Mensagens estão mistas (inglês/português). Padronize para um idioma (sug.: PT-BR).
- Para
!id, o mais adequado seriaBadRequestException, nãoNotFoundException.if (!id) { - throw new NotFoundException('Invalid company ID'); + throw new BadRequestException('ID de empresa inválido'); } @@ - if (!company) { - throw new NotFoundException('Company not found'); + if (!company) { + throw new NotFoundException('Empresa não encontrada'); }
1-170: Padronizar mensagens de erro para PT-BRMensagens de NotFoundException estão misturadas (EN/PT). Padronizar todas para PT-BR.
- src/modules/company/repository/company-repository.ts — linha 45: 'Invalid company ID' → sugerir: 'ID da empresa inválido'
- src/modules/company/repository/company-repository.ts — linha 52: 'Company not found' → sugerir: 'Empresa não encontrada'
- src/modules/company/repository/company-repository.ts — linha 64: 'Empresa não encontrada' (manter)
- src/modules/company/repository/company-repository.ts — linha 104: 'Company not found' → sugerir: 'Empresa não encontrada'
- src/modules/company/repository/company-repository.ts — linha 152: 'Empresa não encontrada' (manter)
Considere centralizar mensagens em constantes ou i18n para facilitar auditoria.
src/modules/mails/templates/health.hbs (1)
1-1: Padronizar locale HTML para pt-BRUse o código de idioma canônico (BCP 47) com maiúsculas:
pt-BR.-<html lang='pt-br'> +<html lang='pt-BR'>src/modules/mails/templates/confirmEmailCompany.hbs (3)
18-19: Layout mais consistente em clientes de e-mailEvite
vmin. Use container fluido com largura máxima (padrão ~600px) para compatibilidade.- style='border: 1px solid #ccc; width: 90vmin; padding-bottom: 50px; position: relative; margin: auto; margin-bottom: 10px' + style='border: 1px solid #ccc; max-width: 600px; width: 100%; padding-bottom: 50px; margin: 0 auto 10px'
43-47: Melhorias no botão/CTAAdicione
relpor boas práticas quandotarget="_blank"e garanta área clicável consistente.- <a - style='background-color: #046AD0; color: #D7D9D7; padding: 12px 16px; border-style: none; border-radius: 8px; text-decoration: none; cursor: pointer' - href='{{url}}' - target='_blank' - > + <a + style='display:inline-block; background-color: #046AD0; color: #D7D9D7; padding: 12px 16px; border: 0; border-radius: 8px; text-decoration: none; cursor: pointer; margin: 20px 0' + href='{{url}}' + target='_blank' + rel='noopener noreferrer' + >
1-1: Padronizar locale HTML para pt-BR-<html lang='pt-br'> +<html lang='pt-BR'>src/modules/mails/templates/create.hbs (1)
1-1: Padronizar locale HTML para pt-BR-<html lang='pt-br'> +<html lang='pt-BR'>src/modules/mails/templates/passwordupdate.hbs (1)
6-11: Fonte carregada não é utilizada
Interé carregada mas obodyusa Arial. Ou use a fonte ou remova os links para reduzir peso.Opção A — usar Inter:
- <body style='font-family: "Arial", sans-serif'> + <body style='font-family: "Inter", Arial, sans-serif'>Opção B — remover recursos não usados:
- <link rel='preconnect' href='https://fonts.googleapis.com' /> - <link rel='preconnect' href='https://fonts.gstatic.com' crossorigin /> - <link - href='https://fonts.googleapis.com/css2?family=Inter:wght@100;200;300;400;500;600;700;800;900&display=swap' - rel='stylesheet' - />Also applies to: 13-13
src/modules/user/dtos/create-user.dto.ts (3)
34-36: Decorator duplicado (@IsString)@IsNotEmpty() @IsString() - @IsString() @MaxLength(128)
25-31: Adicionar limite de tamanho para e-mailEvite payloads excessivos e alinhe com limites comuns (254 chars).
@IsNotEmpty() @IsEmail() + @MaxLength(254) @ApiProperty({ description: 'E-mail do usuário.', example: 'johnsnow@outlook.com', })
57-66: Replicar restrições em confirmPasswordGaranta consistência de tamanho com
password.@IsNotEmpty() @IsString() + @MaxLength(128) @ApiProperty({ description: 'Confirmação de senha', example: 'Abcd@1234', })src/modules/mails/templates/send.hbs (1)
1-13: Compatibilidade de e-mail: avaliar DOCTYPE e metadados.A ausência de DOCTYPE e de um
<title>pode afetar renderização em alguns clientes (especialmente Outlook). Opcionalmente, reintroduza DOCTYPE e um<title>curto.src/modules/mails/templates/confirmEmailUser.hbs (1)
8-8: Defina um título da página para melhor compatibilidade.O
<title>está vazio. Preencha com algo como “Confirmação de e-mail — SouJunior”.- <title></title> + <title>Confirmação de e-mail — SouJunior</title>src/modules/mails/templates/jobsAlert.hbs (2)
206-209: Melhore a segurança/UX dos links de vaga.Abra em nova aba e previna acesso ao
window.opener. Ajuda também na experiência do usuário.- <p><a href='{{this.link}}'>Ver + <p><a href='{{this.link}}' target='_blank' rel='noopener noreferrer'>Ver mais detalhes</a></p>
26-33: CSS com :hover pode não ser suportado por todos os clientes.Efeitos
:hovere sombras podem ser ignorados (especialmente Outlook). Ok manter, mas não dependa deles para compreensão/ação.src/modules/user/user.controller.ts (1)
105-109: Evite import dinâmico para lançar ForbiddenException.Use import estático; reduz overhead e segue o padrão NestJS.
- if (user.id !== id && user.type !== 'admin') { - throw new (await import('@nestjs/common')).ForbiddenException( - 'Acesso negado.', - ); - } + if (user.id !== id && user.type !== 'admin') { + throw new ForbiddenException('Acesso negado.'); + }Adicione o import no topo do arquivo:
import { ForbiddenException } from '@nestjs/common';src/modules/user/services/create-user.service.ts (1)
40-69: Padronização de tratamento de duplicidade: considere confiar no unique + catch.O pré-check duplica consultas e ainda está sujeito a race. Opcionalmente, remova o pré-check e retorne 409 somente no catch, mantendo a mensagem consistente.
Posso abrir um PR de follow-up removendo o pré-check e adicionando testes de concorrência?
src/modules/user/repository/user.repository.ts (4)
37-39: Padronize o tratamento de erro também em getRawAndEntities.Hoje só
getCount()usahandleError. Aplique o mesmo paragetRawAndEntities()para consistência.- const { entities } = await queryBuilder.getRawAndEntities(); + const { entities } = await queryBuilder.getRawAndEntities().catch(handleError);
61-74: Atualize somente os campos necessários; evite salvar a entidade inteira.
save({...userExists, ...data})pode sobrescrever campos inadvertidos e aumenta a carga de escrita. Prefiraupdate(id, data).- const bodyToUpdate = { - ...userExists, - ...data, - }; - await this.usersRepository.save(bodyToUpdate).catch(handleError); - return; + await this.usersRepository.update(user.id, data).catch(handleError); + return;
76-80: Considere sinalizar 404 em deleção sem efeito.
delete(id)não lança erro quando nada é afetado. Para UX/API consistente, converta emNotFoundExceptionquandoaffected === 0.- await this.usersRepository.delete(id).catch(handleError); + const result = await this.usersRepository.delete(id).catch(handleError); + if (!result?.affected) { + throw new NotFoundException('User not found'); + }
72-80: NIT:return;desnecessário em métodosvoid.Pode remover para reduzir ruído.
- await this.usersRepository.save(bodyToUpdate).catch(handleError); - return; + await this.usersRepository.save(bodyToUpdate).catch(handleError);- return;src/shared/Swagger/decorators/savedjobs/classes/meta-pagination.swagger.ts (1)
3-24: Evitar duplicação do schema de paginaçãoJá existe (ou existia)
MetaPaginationSwaggerem outros módulos (ex.: users). Centralizar em um único arquivo compartilhado evita divergência futura de campos/exemplos.src/shared/Swagger/decorators/savedjobs/classes/list-response.swagger.ts (1)
6-10: Compat com @nestjs/swagger: usar factory para arraysPara evitar problemas de avaliação de metadados, prefira a forma com função.
export class SavedJobsListResponseSwagger { - @ApiProperty({ type: [SavedJobSwagger] }) + @ApiProperty({ type: () => [SavedJobSwagger] }) data: SavedJobSwagger[]; - @ApiProperty({ type: MetaPaginationSwagger }) + @ApiProperty({ type: () => MetaPaginationSwagger }) meta: MetaPaginationSwagger; }src/shared/Swagger/decorators/savedjobs/view-savedjobs.swagger.decorator.ts (2)
12-14: Descrição 200 com “Exemplo de sucesso:” sem exemploOu remova a parte do exemplo ou adicione um
content/example. Sugestão mínima:- description: 'Sucesso na rota. Exemplo de sucesso: ', + description: 'Sucesso na rota.',
1-3: Documentar filtros e paginação no SwaggerAdicionar
ApiQueryparapage,take,order,orderByColumn,userId,jobIdmelhora a DX.-import { ApiOperation, ApiResponse } from '@nestjs/swagger'; +import { ApiOperation, ApiResponse, ApiQuery } from '@nestjs/swagger'; @@ return applyDecorators( + ApiQuery({ name: 'page', required: false, type: Number, example: 1 }), + ApiQuery({ name: 'take', required: false, type: Number, example: 10 }), + ApiQuery({ name: 'order', required: false, enum: ['ASC', 'DESC'], example: 'ASC' }), + ApiQuery({ name: 'orderByColumn', required: false, type: String, example: 'createdAt' }), + ApiQuery({ name: 'userId', required: false, type: String, description: 'UUID do usuário' }), + ApiQuery({ name: 'jobId', required: false, type: String, description: 'UUID v4 da vaga' }), ApiResponse({ status: HttpStatus.OK, - description: 'Sucesso na rota. Exemplo de sucesso: ', + description: 'Sucesso na rota.', type: SavedJobsListResponseSwagger, }), @@ ApiOperation({ summary: 'Visualizar todas as vagas salvas.', }), );Also applies to: 9-14, 30-33
src/modules/savedjobs/services/find-all-savedjobs.service.ts (2)
30-35: Log estruturado e preservação da causaAjuste o
logger.errorpara sempre enviar stack string e preserve a causa na exceção 500 (Nest suportacause).- logger.error( - 'Erro inesperado ao buscar vagas salvas', - error?.stack || error, - ); - throw new InternalServerErrorException('Internal server error'); + this.logger.error( + 'Erro inesperado ao buscar vagas salvas', + error instanceof Error ? error.stack : undefined, + ); + throw new InternalServerErrorException('Internal server error', { + cause: error as unknown, + });
34-35: Idiomas consistentes (PT vs EN)Mensagem lançada é EN (“Internal server error”), enquanto docs/descrições estão PT. Considere unificar para PT-BR.
src/shared/Swagger/decorators/savedjobs/classes/saved-job.swagger.ts (1)
4-17: Enriquecer o schema: format/readonly e tornar expiresAt opcionalSugestões para melhorar a documentação e interoperabilidade do OpenAPI:
- Marcar IDs com
format: 'uuid'.- Garantir datas como
type: String+format: 'date-time'.- Marcar campos gerados pelo servidor como
readOnly: true.- Tornar
expiresAtopcional/nullable(caso possa não existir).export class SavedJobSwagger { - @ApiProperty({ example: 'uuid-saved-job-id' }) + @ApiProperty({ + example: 'a3f9c1e2-9c3a-4b6e-8f41-0a7b2f9d1c23', + format: 'uuid', + description: 'Identificador da vaga salva', + readOnly: true, + }) id: string; - @ApiProperty({ example: 'uuid-user-id' }) + @ApiProperty({ + example: 'b8c64b42-2f75-4a9d-9dc7-7b2b5f1a9c0d', + format: 'uuid', + description: 'Identificador do usuário', + }) userId: string; - @ApiProperty({ example: 'uuid-job-id' }) + @ApiProperty({ + example: '6d2e7f0a-5a4b-4b7c-8f0a-1b2c3d4e5f60', + format: 'uuid', + description: 'Identificador da vaga', + }) jobId: string; - @ApiProperty({ example: '2025-09-12T12:00:00.000Z' }) + @ApiProperty({ + example: '2025-09-12T12:00:00.000Z', + type: String, + format: 'date-time', + description: 'Data em que a vaga foi salva', + readOnly: true, + }) savedAt: Date; - @ApiProperty({ example: '2025-10-12T12:00:00.000Z' }) + @ApiProperty({ + example: '2025-10-12T12:00:00.000Z', + type: String, + format: 'date-time', + description: 'Data de expiração da vaga salva (se aplicável)', + required: false, + nullable: true, + }) expiresAt: Date; }src/modules/savedjobs/savedjobs.controller.ts (5)
111-128: Swagger duplicado/inconsistente: exemploitems/totalconflita comSavedJobsListResponseSwagger(data/meta)O decorator
@SwaggerFindSavedJobs()já defineApiOperatione o schema de sucesso. Remova os blocos extras ou alinhe o exemplo ao formatodata/meta.- @SwaggerFindSavedJobs() - @ApiOperation({ - summary: 'Obtenha todos os trabalhos salvos com filtros e paginação.', - }) - @ApiResponse({ - status: HttpStatus.OK, - description: 'Lista de trabalhos salvos', - schema: { - example: { - items: [ - /* exemplo de lista de jobs */ - ], - total: 10, - page: 1, - pageSize: 10, - }, - }, - }) + @SwaggerFindSavedJobs()
91-93: Padronizar idioma e especificar formato UUID no parâmetroPadronize mensagens para pt-BR e indique o
format: 'uuid'no@ApiParam.- @ApiOperation({ summary: 'Delete a saved job' }) - @ApiParam({ name: 'id', description: 'The ID of the saved job to delete' }) + @ApiOperation({ summary: 'Excluir vaga salva' }) + @ApiParam({ + name: 'id', + description: 'ID da vaga salva a ser excluída', + schema: { format: 'uuid' }, + })
48-53: Reforçar validação: adicionarforbidUnknownValues: trueEvita validação de payloads não-objeto ou estranhos que possam bypassar regras.
@UsePipes( new ValidationPipe({ whitelist: true, forbidNonWhitelisted: true, transform: true, + forbidUnknownValues: true, }), )@UsePipes( new ValidationPipe({ whitelist: true, forbidNonWhitelisted: true, transform: true, transformOptions: { enableImplicitConversion: true }, + forbidUnknownValues: true, }), )Also applies to: 104-110
74-87: Evitar capturar e converter todas as exceções em 500Os
try/catchatuais transformam qualquer erro em 500, mascarando erros de validação/negócio (400/404/409). Deixe propagar exceções conhecidas ou normalize via filtros globais.- try { - const savedJob = await this.savedJobsService.saveJob({ - ...createSavedJobDto, - userId: user.id, - }); - return { - message: 'Sua vaga foi salva com sucesso!', - statusCode: HttpStatus.CREATED, - savedJob: savedJob, - }; - } catch (error) { - throw new HttpException( - `Erro ao salvar vaga: ${error.message || 'erro desconhecido'}`, - HttpStatus.INTERNAL_SERVER_ERROR, - ); - } + const savedJob = await this.savedJobsService.saveJob({ + ...createSavedJobDto, + userId: user.id, + }); + return { + message: 'Sua vaga foi salva com sucesso!', + statusCode: HttpStatus.CREATED, + savedJob, + };- try { - const savedJobs = await this.findAllSavedJobsService.getAllSavedJobs( - pageOptionsDto, - { ...query, userId: user.id }, - ); - return savedJobs; - } catch (error) { - throw new HttpException( - `Erro ao buscar vagas salvas: ${error.message || 'erro desconhecido'}`, - HttpStatus.INTERNAL_SERVER_ERROR, - ); - } + const savedJobs = await this.findAllSavedJobsService.getAllSavedJobs( + pageOptionsDto, + { ...query, userId: user.id }, + ); + return savedJobs;Also applies to: 144-148
88-99: DELETE: considerar 204 No Content e documentação correspondentePara exclusão sem body, prefira
204e ajuste Swagger.@Delete(':id') @UseGuards(AuthGuard('jwt')) @ApiBearerAuth() -@ApiOperation({ summary: 'Excluir vaga salva' }) +@ApiOperation({ summary: 'Excluir vaga salva' }) +@HttpCode(HttpStatus.NO_CONTENT) @ApiParam({ name: 'id', description: 'ID da vaga salva a ser excluída', schema: { format: 'uuid' }, }) async deleteSavedJob( @Param('id', new ParseUUIDPipe()) id: string, @LoggedUser() user: UsersEntity, ) { - return this.deleteSavedJobsService.execute({ id }, user.id); + await this.deleteSavedJobsService.execute({ id }, user.id); }Documentação_Sou_Junior.json (1)
891-893: Evitar dados de ambiente “reais” em export público.Além dos tokens, alinhar o campo "local" ao padrão da app (README usa 3000; aqui há 3000/3001/5005). Recomendo unificar para 3000 em todos os ambientes do Insomnia (ou deixar vazio para o dev configurar).
Also applies to: 926-928, 942-944, 958-960, 974-976, 990-992, 1006-1008
src/database/migrations/1753987121418-InitialSchema.ts (2)
54-58: E-mail de empresas sem constraint de unicidade.Risco de duplicidade operacional. Sugiro migration posterior adicionando UNIQUE em
tb_companies.email(e, se aplicável,cnpj).Exemplo (nova migration):
ALTER TABLE "tb_companies" ADD CONSTRAINT "UQ_tb_companies_email" UNIQUE ("email"); -- Opcional: -- ALTER TABLE "tb_companies" ADD CONSTRAINT "UQ_tb_companies_cnpj" UNIQUE ("cnpj");
105-160: Índices ausentes em FKs podem degradar consultas.Postgres não indexa FKs automaticamente. Para joins/filters frequentes, considere índices em:
- tb_comments(user_id), tb_comments(job_id)
- tb_curriculum(user_id)
- tb_saved_jobs(userId), tb_saved_jobs(jobId) (já existem)
- tb_applications(job_id, user_id, curriculum_id)
- tb_candidacies(user_id, job_id)
- tb_jobs(company_id)
Exemplo (nova migration):
CREATE INDEX IF NOT EXISTS "IDX_tb_comments_user" ON "tb_comments" ("user_id"); CREATE INDEX IF NOT EXISTS "IDX_tb_comments_job" ON "tb_comments" ("job_id"); CREATE INDEX IF NOT EXISTS "IDX_tb_curriculum_user" ON "tb_curriculum" ("user_id"); CREATE INDEX IF NOT EXISTS "IDX_tb_applications_job" ON "tb_applications" ("job_id"); CREATE INDEX IF NOT EXISTS "IDX_tb_applications_user" ON "tb_applications" ("user_id"); CREATE INDEX IF NOT EXISTS "IDX_tb_applications_curriculum" ON "tb_applications" ("curriculum_id"); CREATE INDEX IF NOT EXISTS "IDX_tb_candidacies_user" ON "tb_candidacies" ("user_id"); CREATE INDEX IF NOT EXISTS "IDX_tb_candidacies_job" ON "tb_candidacies" ("job_id"); CREATE INDEX IF NOT EXISTS "IDX_tb_jobs_company" ON "tb_jobs" ("company_id");Also applies to: 163-263
README.md (3)
9-10: Adicionar texto alternativo (alt) na imagem do cabeçalho.Conforme MD045, inclua
altpara acessibilidade.-<img width="100%" src="https://capsule-render.vercel.app/api?type=waving&width=100%&color=2088f2&fontColor=ffffff&height=300§ion=header&text=Sou%20Junior&fontSize=90&animation=fadeIn&fontAlignY=38&desc=Projeto%20Opensource%20para%20melhorar%20o%20match%20entre%20profissionais%20Juniors%20e%20Empresas!&descAlignY=61&descAlign=52" /> +<img width="100%" alt="Sou Junior - banner" src="https://capsule-render.vercel.app/api?type=waving&width=100%&color=2088f2&fontColor=ffffff&height=300§ion=header&text=Sou%20Junior&fontSize=90&animation=fadeIn&fontAlignY=38&desc=Projeto%20Opensource%20para%20melhorar%20o%20match%20entre%20profissionais%20Juniors%20e%20Empresas!&descAlignY=61&descAlign=52" />
365-366: Ajuste de copy e link da âncora.Frase truncada e âncora inconsistente. Sugestão:
-Se você tiver algum feedback, por favor nos deixe saber por meio do nosso fazendo uma [contribuição](#contribuição). +Se você tiver feedback, por favor abra uma issue ou faça uma [contribuição](#como_contribuir).
182-199: Seção DevOps: transformar observações em ações rastreáveis.As notas sinalizam riscos (deploy em PRs para main, múltiplos alvos — ECR/Railway). Recomendo:
- Converter cada aviso em issue com checklist (ajustar gatilhos do workflow, definir único registro de deploy, clarificar se este repo ainda é fonte da API).
- Documentar a estratégia de deploy (quem dispara, quando, para onde) no README ou docs/operational.md.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (46)
.eslintrc.js(1 hunks).github/workflows/deploy-hmg.yml(1 hunks).github/workflows/deploy-prod.yml(1 hunks).prettierrc(1 hunks)Documentação_Sou_Junior.json(1 hunks)README.md(8 hunks)docker-compose-vm.yml(1 hunks)src/app.controller.ts(2 hunks)src/app.module.ts(2 hunks)src/database/entities/applications.entity.ts(2 hunks)src/database/entities/comments.entity.ts(2 hunks)src/database/entities/companies.entity.ts(1 hunks)src/database/entities/languages.entity.ts(1 hunks)src/database/migrations/1699999999999-AddUniqueEmailToCompanies.ts(1 hunks)src/database/migrations/1753987121418-InitialSchema.ts(1 hunks)src/modules/comment/repository/comment.repository.ts(1 hunks)src/modules/company/repository/company-repository.ts(5 hunks)src/modules/curriculum/curriculum.controller.ts(1 hunks)src/modules/curriculum/curriculum.service.ts(2 hunks)src/modules/curriculum/repository/curriculum-repository.ts(1 hunks)src/modules/jobs/jobs.controller.ts(4 hunks)src/modules/jobs/repository/job.repository.ts(4 hunks)src/modules/jobs/services/get-all-jobs-from-logged-company.service.ts(1 hunks)src/modules/mails/templates/confirmEmailCompany.hbs(1 hunks)src/modules/mails/templates/confirmEmailUser.hbs(1 hunks)src/modules/mails/templates/create.hbs(1 hunks)src/modules/mails/templates/health.hbs(1 hunks)src/modules/mails/templates/jobsAlert.hbs(1 hunks)src/modules/mails/templates/passwordupdate.hbs(1 hunks)src/modules/mails/templates/send.hbs(1 hunks)src/modules/reports/repository/reports.repository.ts(1 hunks)src/modules/reports/services/find-all-reports.service.ts(1 hunks)src/modules/savedjobs/dtos/get-all-savedjobs.dto.ts(1 hunks)src/modules/savedjobs/savedjobs.controller.ts(5 hunks)src/modules/savedjobs/services/find-all-savedjobs.service.ts(1 hunks)src/modules/user/dtos/create-user.dto.ts(3 hunks)src/modules/user/repository/user.repository.ts(4 hunks)src/modules/user/services/create-user.service.ts(2 hunks)src/modules/user/user.controller.ts(3 hunks)src/shared/Swagger/decorators/jobs/get-all-jobs-of-logged-company.swagger copy.ts(0 hunks)src/shared/Swagger/decorators/jobs/get-all-jobs-of-logged-company.swagger.ts(1 hunks)src/shared/Swagger/decorators/savedjobs/classes/list-response.swagger.ts(1 hunks)src/shared/Swagger/decorators/savedjobs/classes/meta-pagination.swagger.ts(1 hunks)src/shared/Swagger/decorators/savedjobs/classes/saved-job.swagger.ts(1 hunks)src/shared/Swagger/decorators/savedjobs/view-savedjobs.swagger.decorator.ts(1 hunks)src/shared/Swagger/internal-server-error.swagger.ts(1 hunks)
💤 Files with no reviewable changes (1)
- src/shared/Swagger/decorators/jobs/get-all-jobs-of-logged-company.swagger copy.ts
✅ Files skipped from review due to trivial changes (4)
- .eslintrc.js
- .prettierrc
- .github/workflows/deploy-prod.yml
- .github/workflows/deploy-hmg.yml
🚧 Files skipped from review as they are similar to previous changes (5)
- src/modules/savedjobs/dtos/get-all-savedjobs.dto.ts
- src/modules/jobs/jobs.controller.ts
- src/database/entities/languages.entity.ts
- src/database/entities/comments.entity.ts
- src/app.module.ts
🧰 Additional context used
🧬 Code graph analysis (14)
src/shared/Swagger/decorators/savedjobs/classes/list-response.swagger.ts (2)
src/shared/Swagger/decorators/savedjobs/classes/saved-job.swagger.ts (1)
SavedJobSwagger(3-18)src/shared/Swagger/decorators/savedjobs/classes/meta-pagination.swagger.ts (1)
MetaPaginationSwagger(3-24)
src/modules/user/user.controller.ts (2)
src/modules/auth/decorator/logged-user.decorator.ts (1)
LoggedUser(7-18)src/modules/user/repository/user.repository.ts (1)
updatePassword(133-147)
src/modules/comment/repository/comment.repository.ts (8)
src/modules/comment/services/create-comment.service.ts (1)
Injectable(7-32)src/modules/comment/services/get-comment-by-id.service.ts (1)
Injectable(4-17)src/modules/comment/services/delete-comment.service.ts (1)
Injectable(4-17)src/modules/comment/services/update-comment.service.ts (1)
Injectable(5-18)src/modules/comment/services/get-all-comments.service.ts (1)
Injectable(4-17)src/modules/comment/dtos/create-comment.dto.ts (1)
CreateCommentDto(4-29)src/shared/utils/handle-error.util.ts (1)
handleError(3-14)src/modules/comment/dtos/update-comment.dto.ts (1)
UpdateCommentDto(3-8)
src/shared/Swagger/decorators/savedjobs/view-savedjobs.swagger.decorator.ts (4)
src/shared/Swagger/decorators/savedjobs/classes/list-response.swagger.ts (1)
SavedJobsListResponseSwagger(5-11)src/shared/Swagger/unauthorized.swagger.ts (1)
UnauthorizedSwagger(2-8)src/shared/Swagger/bad-request.swagger.ts (1)
BadRequestSwagger(2-5)src/shared/Swagger/internal-server-error.swagger.ts (1)
InternalServerErrorSwagger(3-6)
src/modules/jobs/services/get-all-jobs-from-logged-company.service.ts (1)
src/modules/jobs/interfaces/interfaces.ts (1)
IJobsResponse(9-11)
src/modules/curriculum/curriculum.controller.ts (1)
src/modules/auth/decorator/logged-user.decorator.ts (1)
LoggedUser(7-18)
src/modules/user/repository/user.repository.ts (1)
src/shared/utils/handle-error.util.ts (1)
handleError(3-14)
src/modules/curriculum/repository/curriculum-repository.ts (2)
src/modules/curriculum/types/create-curriculum.type.ts (1)
CreateCurriculumType(3-7)src/shared/utils/handle-error.util.ts (1)
handleError(3-14)
src/modules/reports/repository/reports.repository.ts (7)
src/modules/reports/services/find-all-reports.service.ts (1)
Injectable(4-17)src/modules/reports/services/create-report.service.ts (1)
Injectable(7-45)src/modules/reports/services/find-report-by-id.service.ts (1)
Injectable(5-18)src/modules/reports/services/delete-report.service.ts (1)
Injectable(5-18)src/modules/reports/services/update-report.service.ts (1)
Injectable(6-19)src/shared/utils/handle-error.util.ts (1)
handleError(3-14)src/modules/reports/types/find-by-params.type.ts (1)
ReportParamsType(1-4)
src/app.controller.ts (1)
src/shared/Swagger/decorators/app/health-check.swagger.decorator.ts (1)
SwaggerHealthCheck(5-16)
src/modules/savedjobs/services/find-all-savedjobs.service.ts (4)
src/modules/savedjobs/repository/savedjobs.repository.ts (1)
Injectable(8-46)src/shared/pagination/pageOptions.dto.ts (1)
PageOptionsDto(6-42)src/modules/savedjobs/dtos/get-all-savedjobs.dto.ts (1)
GetAllSavedJobsDto(4-23)src/shared/pagination/page.dto.ts (1)
PageDto(4-14)
src/modules/jobs/repository/job.repository.ts (5)
src/modules/jobs/services/get-all-jobs-from-logged-company.service.ts (1)
Injectable(6-34)src/modules/jobs/services/delete-job.service.ts (1)
Injectable(6-34)src/modules/jobs/services/get-one-job-by-id.service.ts (1)
Injectable(4-21)src/modules/jobs/services/update-job.service.ts (1)
Injectable(5-22)src/shared/utils/handle-error.util.ts (1)
handleError(3-14)
src/modules/savedjobs/savedjobs.controller.ts (3)
src/modules/savedjobs/dtos/create-savedJob-dto.ts (1)
CreateSavedJobDto(3-11)src/modules/auth/decorator/logged-user.decorator.ts (1)
LoggedUser(7-18)src/shared/Swagger/decorators/savedjobs/view-savedjobs.swagger.decorator.ts (1)
SwaggerFindSavedJobs(8-34)
src/modules/company/repository/company-repository.ts (2)
src/shared/utils/handle-error.util.ts (1)
handleError(3-14)src/modules/company/dtos/update-my-password.dto.ts (1)
UpdateMyPasswordDto(5-39)
🪛 markdownlint-cli2 (0.17.2)
README.md
9-9: Images should have alternate text (alt text)
(MD045, no-alt-text)
🪛 Gitleaks (8.27.2)
Documentação_Sou_Junior.json
[high] 892-892: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.
(jwt)
[high] 927-927: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.
(jwt)
[high] 943-943: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.
(jwt)
[high] 959-959: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.
(jwt)
[high] 975-975: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.
(jwt)
[high] 991-991: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.
(jwt)
[high] 1007-1007: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.
(jwt)
src/shared/Swagger/decorators/savedjobs/classes/meta-pagination.swagger.ts
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (1)
src/modules/mails/templates/confirmEmailUser.hbs (1)
269-280: Texto incorreto para o contexto (fala em “recuperar sua senha” num e-mail de confirmação).Mesma observação já feita anteriormente; ainda persiste neste trecho.
Ajuste a cópia para confirmação de cadastro/e‑mail:
- <p - class='no-spacing' - style='color:#5d5f5d; line-height: 22px' - > - Se não realizou nenhuma solicitação - para recuperar sua senha na plataforma - SouJunior, por favor, desconsidere - este e-mail. - </p> + <p + class='no-spacing' + style='color:#5d5f5d; line-height: 22px' + > + Se não realizou nenhuma solicitação de cadastro ou + confirmação de e-mail na plataforma SouJunior, + por favor, desconsidere este e-mail. + </p>
🧹 Nitpick comments (8)
src/modules/mails/templates/jobsAlert.hbs (4)
186-214: Sem fallback quando não há vagas.Quando
jobsé vazio, o e-mail fica sem conteúdo relevante.- {{#each jobs}} + {{#each jobs}} <li> ... </li> - {{/each}} + {{else}} + <li> + <p>Nenhuma vaga encontrada no momento.</p> + </li> + {{/each}}
37-39: Adicionar preheader oculto para melhorar preview em clientes de e-mail.Ajuda na taxa de abertura e contexto.
<body style='background-color: #ffffff; margin: 0; padding: 0; -webkit-text-size-adjust: none; text-size-adjust: none;' > + <div style="display:none!important;visibility:hidden;opacity:0;color:transparent;height:0;width:0;overflow:hidden;mso-hide:all"> + Encontramos novas vagas para você no SouJunior. + </div>
101-105: Imagens sem dimensões e sem display:block (renderização instável em Outlook/Gmail).Defina width/height e
display:block;height:auto;border:0;para evitar reflow e gaps; dimensione ícones (p.ex. 24x24).- <img + <img data-imagetype='External' src='https://vagas-dev.s3.amazonaws.com/1683042170644logo2.png' - alt='Logo' - /> + alt='Logo' + style='display:block;height:auto;border:0;outline:none;text-decoration:none' + />Repita para a imagem de “sucesso” e para os ícones, acrescentando
width/heightnos ícones (ex.:width='24' height='24').Confirme se os assets em
vagas-dev.s3.amazonaws.comsão públicos/estáveis em produção; se forem de ambiente de DEV, mova para um bucket/CDN produtivo.Also applies to: 133-138, 337-358
26-33: CSS de hover tem suporte limitado em clientes de e‑mail.
ul li:hovermelhora UX em poucos clientes; não dependa disso para affordance essencial.src/modules/mails/templates/confirmEmailUser.hbs (4)
203-209: Button dentro de link é pouco compatível; use estilizado como botão.Melhora renderização em Outlook/GMX/Yahoo e evita comportamento inconsistente.
- <a href='{{url}}' target='_blank'> - <button - style='background-color: #046ad0; color: #d7d9d7; font-size: 1.2em; padding: 16px 16px; border-style: none; border-radius: 8px' - > - Confirmar meu e-mail - </button> - </a> + <a + href='{{url}}' + target='_blank' + rel='noopener noreferrer' + style='display:inline-block;background-color:#046ad0;color:#d7d9d7;font-size:1.2em;padding:16px 16px;border-radius:8px;text-decoration:none' + aria-label='Confirmar meu e-mail no SouJunior' + > + Confirmar meu e-mail + </a>Garanta que
{{url}}é absoluto (https://…) e já inclui quaisquer tokens de segurança (não exponha PII em query).
31-33: Inclua preheader oculto para melhor preview.<body style='background-color: #ffffff; margin: 0; padding: 0; -webkit-text-size-adjust: none; text-size-adjust: none;' > + <div style="display:none!important;visibility:hidden;opacity:0;color:transparent;height:0;width:0;overflow:hidden;mso-hide:all"> + Confirme seu e-mail no SouJunior. + </div>
94-99: Imagens sem width/height e sem display:block.Padronize estilos inline para maior compatibilidade e evite layout shift; dimensione ícones (ex.: 24x24).
- <img + <img data-imagetype='External' src='https://vagas-dev.s3.amazonaws.com/1683042170644logo2.png' - alt='Logo' - /> + alt='Logo' + style='display:block;height:auto;border:0;outline:none;text-decoration:none' + />Se a imagem “sucesso” for puramente decorativa, considere
alt=''para leitores de tela.Also applies to: 123-128, 313-334
254-267: Blocos vazios aumentam peso do e‑mail sem valor.Remova
block-10eblock-17se não houver conteúdo.- <table - class='html_block block-10' - width='100%' - border='0' - cellpadding='0' - cellspacing='0' - role='presentation' - style='mso-table-lspace: 0pt; mso-table-rspace: 0pt;' - > - <tr> - <td class='pad'> - <div ...> - <div class='our-class'> - </div> - </div> - </td> - </tr> - </table> ... - <table - class='html_block block-17' - ... - > - <tr> - <td class='pad'> - <div ...><div class='our-class'> - </div></div> - </td> - </tr> - </table>Also applies to: 399-416
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
docker-compose-vm.yml(1 hunks)src/modules/mails/templates/confirmEmailUser.hbs(1 hunks)src/modules/mails/templates/jobsAlert.hbs(1 hunks)src/modules/mails/templates/passwordupdate.hbs(1 hunks)src/modules/mails/templates/send.hbs(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
- src/modules/mails/templates/passwordupdate.hbs
- docker-compose-vm.yml
- src/modules/mails/templates/send.hbs
🔇 Additional comments (1)
src/modules/mails/templates/jobsAlert.hbs (1)
206-211: Abrir links em nova aba, mitigar tabnabbing e garantir URL absoluta + a11y.Substituir o link no template e garantir que o campo enviado ao template seja uma URL absoluta (normalizar/validar na fonte dos dados).
- <p><a href='{{this.link}}'>Ver - mais detalhes</a></p> + <p><a + href='{{this.link}}' + target='_blank' + rel='noopener noreferrer' + aria-label='Ver mais detalhes sobre {{this.title}}' + >Ver mais detalhes</a></p>Observação: o template é renderizado em src/modules/mails/mail.service.ts (template: './jobsAlert'), e em src/modules/jobs/services/create-job.service.ts há
url: ''— confirme se os objetos dejobsusamlinkouurle normalize/valide para garantir URLs absolutas antes de renderizar.
|


🛠️ Correções de ESLint e Prettier
📋 Resumo das Alterações
Este PR implementa correções de linting e formatação em toda a base de código para melhorar a qualidade e consistência do projeto.
✨ Principais Mudanças
🧹 Limpeza de Código
📂 Arquivos Afetados
app.controller.ts,app.module.ts,app.service.ts📊 Estatísticas
🎯 Benefícios
🔍 Tipo de Mudança
✅ Checklist
Nota: Esta é uma correção de manutenção focada exclusivamente na qualidade do código, sem alterações de funcionalidade.
Summary by CodeRabbit
Novos Recursos
Melhorias
Documentação