nestforge-generator 0.2.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (156) hide show
  1. package/README.md +24 -7
  2. package/README.pt-BR.md +24 -7
  3. package/dist/features/auth-strategy.js +7 -7
  4. package/dist/features/database.js +35 -0
  5. package/dist/features/database.js.map +1 -1
  6. package/dist/features/dependencies.js +3 -3
  7. package/dist/features/language.js +1 -1
  8. package/dist/features/language.js.map +1 -1
  9. package/dist/features/markers.js +11 -11
  10. package/dist/features/markers.js.map +1 -1
  11. package/dist/generator.js +14 -2
  12. package/dist/generator.js.map +1 -1
  13. package/dist/index.js +8 -1
  14. package/dist/index.js.map +1 -1
  15. package/dist/project-name.js +50 -0
  16. package/dist/project-name.js.map +1 -0
  17. package/dist/prompts.js +15 -11
  18. package/dist/prompts.js.map +1 -1
  19. package/package.json +2 -1
  20. package/templates/drizzle/README.md +2 -0
  21. package/templates/drizzle/README.pt-BR.md +2 -0
  22. package/templates/drizzle/drizzle.config.ts +2 -2
  23. package/templates/drizzle/src/auth/auth.controller.ts +35 -35
  24. package/templates/drizzle/src/auth/auth.service.spec.ts +4 -4
  25. package/templates/drizzle/src/auth/auth.service.ts +9 -9
  26. package/templates/drizzle/src/auth/dto/forgot-password.dto.ts +2 -2
  27. package/templates/drizzle/src/auth/dto/login.dto.ts +3 -3
  28. package/templates/drizzle/src/auth/dto/register.dto.ts +4 -4
  29. package/templates/drizzle/src/auth/dto/reset-password.dto.ts +3 -3
  30. package/templates/drizzle/src/auth/guards/github-auth.guard.ts +2 -2
  31. package/templates/drizzle/src/auth/guards/google-auth.guard.ts +2 -2
  32. package/templates/drizzle/src/auth/guards/session-auth.guard.spec.ts +4 -4
  33. package/templates/drizzle/src/auth/guards/session-auth.guard.ts +2 -2
  34. package/templates/drizzle/src/auth/session.service.spec.ts +5 -5
  35. package/templates/drizzle/src/auth/session.service.ts +5 -5
  36. package/templates/drizzle/src/auth/strategies/github.strategy.ts +3 -3
  37. package/templates/drizzle/src/auth/token.service.ts +3 -3
  38. package/templates/drizzle/src/common/filters/http-exception.filter.ts +1 -1
  39. package/templates/drizzle/src/common/guards/permissions.guard.spec.ts +7 -7
  40. package/templates/drizzle/src/common/guards/roles.guard.spec.ts +4 -4
  41. package/templates/drizzle/src/common/middleware/csrf.middleware.spec.ts +5 -5
  42. package/templates/drizzle/src/common/middleware/csrf.middleware.ts +2 -2
  43. package/templates/drizzle/src/common/utils/avatar-storage.util.ts +2 -2
  44. package/templates/drizzle/src/config/env.validation.ts +1 -1
  45. package/templates/drizzle/src/database/seed.ts +4 -4
  46. package/templates/drizzle/src/health/health.controller.ts +2 -2
  47. package/templates/drizzle/src/health/indicators/drizzle-health.indicator.spec.ts +5 -5
  48. package/templates/drizzle/src/health/indicators/drizzle-health.indicator.ts +2 -2
  49. package/templates/drizzle/src/health/indicators/redis-health.indicator.spec.ts +5 -5
  50. package/templates/drizzle/src/health/indicators/redis-health.indicator.ts +3 -3
  51. package/templates/drizzle/src/mail/mail.processor.ts +4 -4
  52. package/templates/drizzle/src/mail/templates/email-templates.ts +7 -7
  53. package/templates/drizzle/src/metrics/metrics.service.ts +3 -3
  54. package/templates/drizzle/src/users/dto/create-user.dto.ts +5 -5
  55. package/templates/drizzle/src/users/dto/find-users-query.dto.ts +3 -3
  56. package/templates/drizzle/src/users/users.controller.ts +21 -21
  57. package/templates/drizzle/src/users/users.service.spec.ts +12 -12
  58. package/templates/drizzle/src/users/users.service.ts +4 -4
  59. package/templates/drizzle/test/auth.e2e-spec.ts +8 -8
  60. package/templates/drizzle/test/session-auth.e2e-spec.ts +9 -9
  61. package/templates/drizzle/test/users.e2e-spec.ts +18 -18
  62. package/templates/prisma/.github/workflows/ci.yml +24 -1
  63. package/templates/prisma/ARCHITECTURE.md +7 -1
  64. package/templates/prisma/ARCHITECTURE.pt-BR.md +7 -1
  65. package/templates/prisma/README.md +11 -7
  66. package/templates/prisma/README.pt-BR.md +11 -7
  67. package/templates/prisma/ROADMAP.md +5 -1
  68. package/templates/prisma/ROADMAP.pt-BR.md +5 -1
  69. package/templates/prisma/TESTING.md +25 -3
  70. package/templates/prisma/TESTING.pt-BR.md +25 -3
  71. package/templates/prisma/docker-compose.yml +29 -1
  72. package/templates/prisma/docs/adding-a-module.md +246 -230
  73. package/templates/prisma/docs/features-markers.md +11 -1
  74. package/templates/prisma/prisma/schema.prisma +4 -0
  75. package/templates/prisma/prisma/seed.ts +46 -46
  76. package/templates/prisma/src/auth/auth.controller.ts +35 -35
  77. package/templates/prisma/src/auth/auth.service.spec.ts +3 -3
  78. package/templates/prisma/src/auth/auth.service.ts +11 -11
  79. package/templates/prisma/src/auth/dto/forgot-password.dto.ts +2 -2
  80. package/templates/prisma/src/auth/dto/login.dto.ts +3 -3
  81. package/templates/prisma/src/auth/dto/register.dto.ts +4 -4
  82. package/templates/prisma/src/auth/dto/reset-password.dto.ts +3 -3
  83. package/templates/prisma/src/auth/guards/github-auth.guard.ts +10 -10
  84. package/templates/prisma/src/auth/guards/google-auth.guard.ts +10 -10
  85. package/templates/prisma/src/auth/guards/session-auth.guard.spec.ts +4 -4
  86. package/templates/prisma/src/auth/guards/session-auth.guard.ts +2 -2
  87. package/templates/prisma/src/auth/session.service.spec.ts +5 -5
  88. package/templates/prisma/src/auth/session.service.ts +5 -5
  89. package/templates/prisma/src/auth/strategies/github.strategy.ts +3 -3
  90. package/templates/prisma/src/auth/token.service.ts +14 -5
  91. package/templates/prisma/src/common/filters/http-exception.filter.ts +34 -34
  92. package/templates/prisma/src/common/guards/permissions.guard.spec.ts +7 -7
  93. package/templates/prisma/src/common/guards/roles.guard.spec.ts +4 -4
  94. package/templates/prisma/src/common/middleware/csrf.middleware.spec.ts +5 -5
  95. package/templates/prisma/src/common/middleware/csrf.middleware.ts +2 -2
  96. package/templates/prisma/src/common/utils/avatar-storage.util.ts +32 -32
  97. package/templates/prisma/src/config/env.validation.ts +1 -1
  98. package/templates/prisma/src/health/health.controller.ts +2 -2
  99. package/templates/prisma/src/health/indicators/prisma-health.indicator.spec.ts +39 -22
  100. package/templates/prisma/src/health/indicators/prisma-health.indicator.ts +24 -19
  101. package/templates/prisma/src/health/indicators/redis-health.indicator.spec.ts +5 -5
  102. package/templates/prisma/src/health/indicators/redis-health.indicator.ts +3 -3
  103. package/templates/prisma/src/mail/mail.processor.ts +4 -4
  104. package/templates/prisma/src/mail/templates/email-templates.ts +7 -7
  105. package/templates/prisma/src/metrics/metrics.service.ts +34 -34
  106. package/templates/prisma/src/users/dto/create-user.dto.ts +12 -12
  107. package/templates/prisma/src/users/dto/find-users-query.dto.ts +12 -12
  108. package/templates/prisma/src/users/users.controller.ts +21 -21
  109. package/templates/prisma/src/users/users.service.spec.ts +11 -11
  110. package/templates/prisma/src/users/users.service.ts +4 -4
  111. package/templates/prisma/test/auth.e2e-spec.ts +8 -8
  112. package/templates/prisma/test/session-auth.e2e-spec.ts +9 -9
  113. package/templates/prisma/test/users.e2e-spec.ts +14 -14
  114. package/templates/typeorm/README.md +2 -0
  115. package/templates/typeorm/README.pt-BR.md +2 -0
  116. package/templates/typeorm/src/auth/auth.controller.ts +35 -35
  117. package/templates/typeorm/src/auth/auth.service.spec.ts +4 -4
  118. package/templates/typeorm/src/auth/auth.service.ts +9 -9
  119. package/templates/typeorm/src/auth/dto/forgot-password.dto.ts +2 -2
  120. package/templates/typeorm/src/auth/dto/login.dto.ts +3 -3
  121. package/templates/typeorm/src/auth/dto/register.dto.ts +4 -4
  122. package/templates/typeorm/src/auth/dto/reset-password.dto.ts +3 -3
  123. package/templates/typeorm/src/auth/guards/github-auth.guard.ts +2 -2
  124. package/templates/typeorm/src/auth/guards/google-auth.guard.ts +2 -2
  125. package/templates/typeorm/src/auth/guards/session-auth.guard.spec.ts +4 -4
  126. package/templates/typeorm/src/auth/guards/session-auth.guard.ts +2 -2
  127. package/templates/typeorm/src/auth/session.service.spec.ts +5 -5
  128. package/templates/typeorm/src/auth/session.service.ts +5 -5
  129. package/templates/typeorm/src/auth/strategies/github.strategy.ts +3 -3
  130. package/templates/typeorm/src/auth/token.service.ts +3 -3
  131. package/templates/typeorm/src/common/filters/http-exception.filter.ts +1 -1
  132. package/templates/typeorm/src/common/guards/permissions.guard.spec.ts +7 -7
  133. package/templates/typeorm/src/common/guards/roles.guard.spec.ts +4 -4
  134. package/templates/typeorm/src/common/middleware/csrf.middleware.spec.ts +5 -5
  135. package/templates/typeorm/src/common/middleware/csrf.middleware.ts +2 -2
  136. package/templates/typeorm/src/common/utils/avatar-storage.util.ts +2 -2
  137. package/templates/typeorm/src/config/env.validation.ts +1 -1
  138. package/templates/typeorm/src/database/data-source.ts +2 -2
  139. package/templates/typeorm/src/database/seed.ts +3 -3
  140. package/templates/typeorm/src/database/typeorm-options.ts +2 -2
  141. package/templates/typeorm/src/health/health.controller.ts +2 -2
  142. package/templates/typeorm/src/health/indicators/redis-health.indicator.spec.ts +5 -5
  143. package/templates/typeorm/src/health/indicators/redis-health.indicator.ts +3 -3
  144. package/templates/typeorm/src/health/indicators/typeorm-health.indicator.spec.ts +4 -4
  145. package/templates/typeorm/src/health/indicators/typeorm-health.indicator.ts +2 -2
  146. package/templates/typeorm/src/mail/mail.processor.ts +4 -4
  147. package/templates/typeorm/src/mail/templates/email-templates.ts +7 -7
  148. package/templates/typeorm/src/metrics/metrics.service.ts +3 -3
  149. package/templates/typeorm/src/users/dto/create-user.dto.ts +5 -5
  150. package/templates/typeorm/src/users/dto/find-users-query.dto.ts +3 -3
  151. package/templates/typeorm/src/users/users.controller.ts +21 -21
  152. package/templates/typeorm/src/users/users.service.spec.ts +12 -12
  153. package/templates/typeorm/src/users/users.service.ts +4 -4
  154. package/templates/typeorm/test/auth.e2e-spec.ts +8 -8
  155. package/templates/typeorm/test/session-auth.e2e-spec.ts +9 -9
  156. package/templates/typeorm/test/users.e2e-spec.ts +18 -18
@@ -60,7 +60,7 @@ export class AuthService {
60
60
  });
61
61
 
62
62
  if (existing) {
63
- throw new ConflictException('E-mail já cadastrado');
63
+ throw new ConflictException('Email already registered');
64
64
  }
65
65
 
66
66
  const passwordHash = await bcrypt.hash(dto.password, 10);
@@ -91,7 +91,7 @@ export class AuthService {
91
91
 
92
92
  if (!user || !user.passwordHash) {
93
93
  throw new UnauthorizedException(
94
- 'Credenciais inválidas',
94
+ 'Invalid credentials',
95
95
  );
96
96
  }
97
97
 
@@ -102,7 +102,7 @@ export class AuthService {
102
102
 
103
103
  if (!passwordMatches) {
104
104
  throw new UnauthorizedException(
105
- 'Credenciais inválidas',
105
+ 'Invalid credentials',
106
106
  );
107
107
  }
108
108
 
@@ -164,7 +164,7 @@ export class AuthService {
164
164
 
165
165
  const genericResponse = {
166
166
  message:
167
- 'Se o e-mail existir, enviaremos instruções de redefinição de senha',
167
+ 'If the email exists, password reset instructions will be sent',
168
168
  };
169
169
 
170
170
  if (!user) {
@@ -214,7 +214,7 @@ export class AuthService {
214
214
  stored.expiresAt < new Date()
215
215
  ) {
216
216
  throw new UnauthorizedException(
217
- 'Token de redefinição inválido ou expirado',
217
+ 'Invalid or expired password reset token',
218
218
  );
219
219
  }
220
220
 
@@ -253,7 +253,7 @@ export class AuthService {
253
253
  );
254
254
 
255
255
  return {
256
- message: 'Senha redefinida com sucesso',
256
+ message: 'Password reset successfully',
257
257
  };
258
258
  }
259
259
 
@@ -271,7 +271,7 @@ export class AuthService {
271
271
  stored.expiresAt < new Date()
272
272
  ) {
273
273
  throw new UnauthorizedException(
274
- 'Token de verificação inválido ou expirado',
274
+ 'Invalid or expired verification token',
275
275
  );
276
276
  }
277
277
 
@@ -292,7 +292,7 @@ export class AuthService {
292
292
  );
293
293
 
294
294
  return {
295
- message: 'E-mail verificado com sucesso',
295
+ message: 'Email verified successfully',
296
296
  };
297
297
  }
298
298
 
@@ -343,4 +343,4 @@ export class AuthService {
343
343
  .update(token)
344
344
  .digest('hex');
345
345
  }
346
- }
346
+ }
@@ -3,7 +3,7 @@ import { z } from 'zod';
3
3
  import { createZodDto } from 'nestjs-zod';
4
4
 
5
5
  export const forgotPasswordSchema = z.object({
6
- email: z.string().email().describe('E-mail da conta a ser recuperada'),
6
+ email: z.string().email().describe('Email of the account to recover'),
7
7
  });
8
8
 
9
- export class ForgotPasswordDto extends createZodDto(forgotPasswordSchema) { }
9
+ export class ForgotPasswordDto extends createZodDto(forgotPasswordSchema) { }
@@ -3,8 +3,8 @@ import { z } from 'zod';
3
3
  import { createZodDto } from 'nestjs-zod';
4
4
 
5
5
  export const loginSchema = z.object({
6
- email: z.string().email().describe('E-mail do usuário'),
7
- password: z.string().min(8).describe('Senha'),
6
+ email: z.string().email().describe('User email'),
7
+ password: z.string().min(8).describe('Password'),
8
8
  });
9
9
 
10
- export class LoginDto extends createZodDto(loginSchema) { }
10
+ export class LoginDto extends createZodDto(loginSchema) { }
@@ -3,9 +3,9 @@ import { z } from 'zod';
3
3
  import { createZodDto } from 'nestjs-zod';
4
4
 
5
5
  export const registerSchema = z.object({
6
- name: z.string().min(2).describe('Nome completo do usuário'),
7
- email: z.string().email().describe('E-mail do usuário'),
8
- password: z.string().min(8).describe('Senha (mínimo 8 caracteres)'),
6
+ name: z.string().min(2).describe('User full name'),
7
+ email: z.string().email().describe('User email'),
8
+ password: z.string().min(8).describe('Password (minimum 8 characters)'),
9
9
  });
10
10
 
11
- export class RegisterDto extends createZodDto(registerSchema) { }
11
+ export class RegisterDto extends createZodDto(registerSchema) { }
@@ -3,8 +3,8 @@ import { z } from 'zod';
3
3
  import { createZodDto } from 'nestjs-zod';
4
4
 
5
5
  export const resetPasswordSchema = z.object({
6
- token: z.string().describe('Token recebido por e-mail'),
7
- password: z.string().min(8).describe('Nova senha (mínimo 8 caracteres)'),
6
+ token: z.string().describe('Token received by email'),
7
+ password: z.string().min(8).describe('New password (minimum 8 characters)'),
8
8
  });
9
9
 
10
- export class ResetPasswordDto extends createZodDto(resetPasswordSchema) { }
10
+ export class ResetPasswordDto extends createZodDto(resetPasswordSchema) { }
@@ -4,7 +4,7 @@ import { AuthGuard } from '@nestjs/passport';
4
4
  @Injectable()
5
5
  export class GithubAuthGuard extends AuthGuard('github') {
6
6
  constructor() {
7
- // API stateless (Bearer token) — não usamos sessão do Passport/Express
7
+ // Stateless API (Bearer token) — Passport/Express sessions are not used.
8
8
  super({ session: false });
9
9
  }
10
- }
10
+ }
@@ -4,7 +4,7 @@ import { AuthGuard } from '@nestjs/passport';
4
4
  @Injectable()
5
5
  export class GoogleAuthGuard extends AuthGuard('google') {
6
6
  constructor() {
7
- // API stateless (Bearer token) — não usamos sessão do Passport/Express
7
+ // Stateless API (Bearer token) — Passport/Express sessions are not used.
8
8
  super({ session: false });
9
9
  }
10
- }
10
+ }
@@ -44,14 +44,14 @@ describe('SessionAuthGuard', () => {
44
44
  return { context, request };
45
45
  }
46
46
 
47
- it('permite acesso a rotas públicas sem sessão', () => {
47
+ it('allows access to public routes without a session', () => {
48
48
  reflector.getAllAndOverride.mockReturnValue(true);
49
49
  const { context } = createContext();
50
50
 
51
51
  expect(guard.canActivate(context)).toBe(true);
52
52
  });
53
53
 
54
- it('rejeita acesso quando não existe usuário na sessão', () => {
54
+ it('rejects access when the session has no user', () => {
55
55
  reflector.getAllAndOverride.mockReturnValue(false);
56
56
  const { context } = createContext();
57
57
 
@@ -60,7 +60,7 @@ describe('SessionAuthGuard', () => {
60
60
  );
61
61
  });
62
62
 
63
- it('permite acesso e disponibiliza o usuário autenticado', () => {
63
+ it('allows access and exposes the authenticated user', () => {
64
64
  reflector.getAllAndOverride.mockReturnValue(false);
65
65
 
66
66
  const user = {
@@ -74,4 +74,4 @@ describe('SessionAuthGuard', () => {
74
74
  expect(guard.canActivate(context)).toBe(true);
75
75
  expect(request.user).toEqual(user);
76
76
  });
77
- });
77
+ });
@@ -27,11 +27,11 @@ export class SessionAuthGuard implements CanActivate {
27
27
  const user = request.session?.user;
28
28
 
29
29
  if (!user) {
30
- throw new UnauthorizedException('Sessão inválida ou expirada');
30
+ throw new UnauthorizedException('Invalid or expired session');
31
31
  }
32
32
 
33
33
  request.user = user;
34
34
 
35
35
  return true;
36
36
  }
37
- }
37
+ }
@@ -31,7 +31,7 @@ describe('SessionService', () => {
31
31
  };
32
32
  }
33
33
 
34
- it('regenera, preenche e salva a sessão', async () => {
34
+ it('regenerates, populates, and saves the session', async () => {
35
35
  const { request, session } = createRequest();
36
36
  const user: SessionUser = {
37
37
  id: 'user-1',
@@ -51,7 +51,7 @@ describe('SessionService', () => {
51
51
  });
52
52
  });
53
53
 
54
- it('emite e salva um novo token CSRF', async () => {
54
+ it('issues and saves a new CSRF token', async () => {
55
55
  const { request, session } = createRequest();
56
56
 
57
57
  const result = await sessionService.issueCsrfToken(request);
@@ -61,14 +61,14 @@ describe('SessionService', () => {
61
61
  expect(session.save).toHaveBeenCalledOnce();
62
62
  });
63
63
 
64
- it('destrói a sessão atual', async () => {
64
+ it('destroys the current session', async () => {
65
65
  const { request, session } = createRequest();
66
66
 
67
67
  const result = await sessionService.destroy(request);
68
68
 
69
69
  expect(session.destroy).toHaveBeenCalledOnce();
70
70
  expect(result).toEqual({
71
- message: 'Logout realizado com sucesso',
71
+ message: 'Logout completed successfully',
72
72
  });
73
73
  });
74
- });
74
+ });
@@ -47,7 +47,7 @@ export class SessionService {
47
47
  if (error) {
48
48
  reject(
49
49
  new InternalServerErrorException(
50
- 'Não foi possível encerrar a sessão',
50
+ 'Unable to end the session',
51
51
  ),
52
52
  );
53
53
  return;
@@ -57,7 +57,7 @@ export class SessionService {
57
57
  });
58
58
  });
59
59
 
60
- return { message: 'Logout realizado com sucesso' };
60
+ return { message: 'Logout completed successfully' };
61
61
  }
62
62
 
63
63
  private async regenerate(request: Request): Promise<void> {
@@ -66,7 +66,7 @@ export class SessionService {
66
66
  if (error) {
67
67
  reject(
68
68
  new InternalServerErrorException(
69
- 'Não foi possível iniciar a sessão',
69
+ 'Unable to start the session',
70
70
  ),
71
71
  );
72
72
  return;
@@ -83,7 +83,7 @@ export class SessionService {
83
83
  if (error) {
84
84
  reject(
85
85
  new InternalServerErrorException(
86
- 'Não foi possível salvar a sessão',
86
+ 'Unable to save the session',
87
87
  ),
88
88
  );
89
89
  return;
@@ -93,4 +93,4 @@ export class SessionService {
93
93
  });
94
94
  });
95
95
  }
96
- }
96
+ }
@@ -25,7 +25,7 @@ export class GithubStrategy extends PassportStrategy(Strategy, 'github') {
25
25
  profile: Profile,
26
26
  done: (error: unknown, user?: OAuthProfile) => void,
27
27
  ) {
28
- // o e-mail pode vir vazio se o usuário deixou o e-mail privado no GitHub
28
+ // The email may be missing when the user keeps it private on GitHub.
29
29
  const email =
30
30
  profile.emails?.[0]?.value ?? `${profile.username}@users.noreply.github.com`;
31
31
 
@@ -33,9 +33,9 @@ export class GithubStrategy extends PassportStrategy(Strategy, 'github') {
33
33
  provider: 'github',
34
34
  providerUserId: profile.id,
35
35
  email,
36
- name: profile.displayName || profile.username || 'Usuário GitHub',
36
+ name: profile.displayName || profile.username || 'GitHub User',
37
37
  };
38
38
 
39
39
  done(null, oauthProfile);
40
40
  }
41
- }
41
+ }
@@ -76,7 +76,7 @@ export class TokenService {
76
76
  stored.expiresAt < new Date()
77
77
  ) {
78
78
  throw new UnauthorizedException(
79
- 'Refresh token inválido ou expirado',
79
+ 'Invalid or expired refresh token',
80
80
  );
81
81
  }
82
82
 
@@ -104,7 +104,7 @@ export class TokenService {
104
104
  },
105
105
  );
106
106
 
107
- return { message: 'Logout realizado com sucesso' };
107
+ return { message: 'Logout completed successfully' };
108
108
  }
109
109
 
110
110
  private hashToken(token: string): string {
@@ -112,4 +112,4 @@ export class TokenService {
112
112
  .update(token)
113
113
  .digest('hex');
114
114
  }
115
- }
115
+ }
@@ -22,7 +22,7 @@ export class HttpExceptionFilter implements ExceptionFilter {
22
22
  const message =
23
23
  exception instanceof HttpException
24
24
  ? exception.getResponse()
25
- : 'Erro interno do servidor';
25
+ : 'Internal server error';
26
26
 
27
27
  response.status(status).json({
28
28
  statusCode: status,
@@ -15,14 +15,14 @@ function createContext(user?: { role: Role }): ExecutionContext {
15
15
  }
16
16
 
17
17
  describe('PermissionsGuard', () => {
18
- it('libera acesso quando a rota não exige nenhuma permissão', () => {
18
+ it('allows access when the route requires no permission', () => {
19
19
  const reflector = { getAllAndOverride: vi.fn().mockReturnValue(undefined) } as unknown as Reflector;
20
20
  const guard = new PermissionsGuard(reflector);
21
21
 
22
22
  expect(guard.canActivate(createContext({ role: Role.USER }))).toBe(true);
23
23
  });
24
24
 
25
- it('libera acesso quando a role do usuário tem a permissão exigida', () => {
25
+ it('allows access when the user role has the required permission', () => {
26
26
  const reflector = {
27
27
  getAllAndOverride: vi.fn().mockReturnValue([Permission.UserRead]),
28
28
  } as unknown as Reflector;
@@ -31,7 +31,7 @@ describe('PermissionsGuard', () => {
31
31
  expect(guard.canActivate(createContext({ role: Role.USER }))).toBe(true);
32
32
  });
33
33
 
34
- it('bloqueia acesso quando a role do usuário não tem a permissão exigida', () => {
34
+ it('blocks access when the user role lacks the required permission', () => {
35
35
  const reflector = {
36
36
  getAllAndOverride: vi.fn().mockReturnValue([Permission.UserDelete]),
37
37
  } as unknown as Reflector;
@@ -40,15 +40,15 @@ describe('PermissionsGuard', () => {
40
40
  expect(guard.canActivate(createContext({ role: Role.USER }))).toBe(false);
41
41
  });
42
42
 
43
- it('exige todas as permissões quando mais de uma é requisitada', () => {
43
+ it('requires all permissions when more than one is requested', () => {
44
44
  const reflector = {
45
45
  getAllAndOverride: vi.fn().mockReturnValue([Permission.UserRead, Permission.UserUpdate]),
46
46
  } as unknown as Reflector;
47
47
  const guard = new PermissionsGuard(reflector);
48
48
 
49
- // MANAGER tem user:read e user:update
49
+ // MANAGER has user:read and user:update
50
50
  expect(guard.canActivate(createContext({ role: Role.MANAGER }))).toBe(true);
51
- // USER só tem user:read
51
+ // USER only has user:read
52
52
  expect(guard.canActivate(createContext({ role: Role.USER }))).toBe(false);
53
53
  });
54
- });
54
+ });
@@ -14,24 +14,24 @@ function createContext(user?: { role: Role }): ExecutionContext {
14
14
  }
15
15
 
16
16
  describe('RolesGuard', () => {
17
- it('libera acesso quando a rota não exige nenhuma role', () => {
17
+ it('allows access when the route requires no role', () => {
18
18
  const reflector = { getAllAndOverride: vi.fn().mockReturnValue(undefined) } as unknown as Reflector;
19
19
  const guard = new RolesGuard(reflector);
20
20
 
21
21
  expect(guard.canActivate(createContext({ role: Role.USER }))).toBe(true);
22
22
  });
23
23
 
24
- it('libera acesso quando o usuário tem a role exigida', () => {
24
+ it('allows access when the user has the required role', () => {
25
25
  const reflector = { getAllAndOverride: vi.fn().mockReturnValue([Role.ADMIN]) } as unknown as Reflector;
26
26
  const guard = new RolesGuard(reflector);
27
27
 
28
28
  expect(guard.canActivate(createContext({ role: Role.ADMIN }))).toBe(true);
29
29
  });
30
30
 
31
- it('bloqueia acesso quando o usuário não tem a role exigida', () => {
31
+ it('blocks access when the user does not have the required role', () => {
32
32
  const reflector = { getAllAndOverride: vi.fn().mockReturnValue([Role.ADMIN]) } as unknown as Reflector;
33
33
  const guard = new RolesGuard(reflector);
34
34
 
35
35
  expect(guard.canActivate(createContext({ role: Role.USER }))).toBe(false);
36
36
  });
37
- });
37
+ });
@@ -36,7 +36,7 @@ describe('CsrfMiddleware', () => {
36
36
  }
37
37
 
38
38
  it.each(['GET', 'HEAD', 'OPTIONS'])(
39
- 'permite o método seguro %s sem token',
39
+ 'allows the safe %s method without a token',
40
40
  (method) => {
41
41
  const request = createRequest(method);
42
42
 
@@ -50,7 +50,7 @@ describe('CsrfMiddleware', () => {
50
50
  },
51
51
  );
52
52
 
53
- it('rejeita uma requisição sem token CSRF', () => {
53
+ it('rejects a request without a CSRF token', () => {
54
54
  const request = createRequest('POST');
55
55
 
56
56
  expect(() =>
@@ -64,7 +64,7 @@ describe('CsrfMiddleware', () => {
64
64
  expect(next).not.toHaveBeenCalled();
65
65
  });
66
66
 
67
- it('rejeita quando o token do header é diferente da sessão', () => {
67
+ it('rejects when the header token differs from the session token', () => {
68
68
  const request = createRequest(
69
69
  'PATCH',
70
70
  'session-token',
@@ -82,7 +82,7 @@ describe('CsrfMiddleware', () => {
82
82
  expect(next).not.toHaveBeenCalled();
83
83
  });
84
84
 
85
- it('permite quando o token corresponde ao valor da sessão', () => {
85
+ it('allows the request when the token matches the session value', () => {
86
86
  const token = 'a'.repeat(64);
87
87
  const request = createRequest('DELETE', token, token);
88
88
 
@@ -94,4 +94,4 @@ describe('CsrfMiddleware', () => {
94
94
 
95
95
  expect(next).toHaveBeenCalledOnce();
96
96
  });
97
- });
97
+ });
@@ -27,7 +27,7 @@ export class CsrfMiddleware implements NestMiddleware {
27
27
  !this.tokensMatch(sessionToken, headerToken)
28
28
  ) {
29
29
  throw new ForbiddenException(
30
- 'Token CSRF inválido ou ausente',
30
+ 'Invalid or missing CSRF token',
31
31
  );
32
32
  }
33
33
 
@@ -43,4 +43,4 @@ export class CsrfMiddleware implements NestMiddleware {
43
43
  timingSafeEqual(expectedBuffer, receivedBuffer)
44
44
  );
45
45
  }
46
- }
46
+ }
@@ -20,7 +20,7 @@ export function avatarFileFilter(
20
20
  ) {
21
21
  if (!ALLOWED_MIME_TYPES.includes(file.mimetype)) {
22
22
  callback(
23
- new BadRequestException('Formato de imagem não suportado (use PNG, JPEG ou WEBP)'),
23
+ new BadRequestException('Unsupported image format (use PNG, JPEG, or WEBP)'),
24
24
  false,
25
25
  );
26
26
  return;
@@ -29,4 +29,4 @@ export function avatarFileFilter(
29
29
  callback(null, true);
30
30
  }
31
31
 
32
- export const AVATAR_MAX_SIZE_BYTES = 2 * 1024 * 1024; // 2MB
32
+ export const AVATAR_MAX_SIZE_BYTES = 2 * 1024 * 1024; // 2MB
@@ -43,7 +43,7 @@ export function validateEnv(config: Record<string, unknown>): EnvConfig {
43
43
  const issues = parsed.error.issues
44
44
  .map((issue) => `${issue.path.join('.')}: ${issue.message}`)
45
45
  .join('\n');
46
- throw new Error(`Variáveis de ambiente inválidas:\n${issues}`);
46
+ throw new Error(`Invalid environment variables:\n${issues}`);
47
47
  }
48
48
 
49
49
  return parsed.data;
@@ -12,9 +12,9 @@ const databaseType = (
12
12
  const databaseUrl = process.env.DATABASE_URL;
13
13
 
14
14
  if (!databaseUrl) {
15
- throw new Error('DATABASE_URL não foi definida');
15
+ throw new Error('DATABASE_URL is not defined');
16
16
  }
17
17
 
18
18
  export default new DataSource(
19
19
  createTypeOrmOptions(databaseType, databaseUrl),
20
- );
20
+ );
@@ -25,7 +25,7 @@ const SEED_USERS: SeedUser[] = [
25
25
  role: Role.MANAGER,
26
26
  },
27
27
  {
28
- name: 'Usuário',
28
+ name: 'User',
29
29
  email: 'user@nestforge.dev',
30
30
  password: 'user1234',
31
31
  role: Role.USER,
@@ -76,6 +76,6 @@ async function seed() {
76
76
  }
77
77
 
78
78
  seed().catch((error: unknown) => {
79
- console.error('Erro ao executar o seed:', error);
79
+ console.error('Error while running the seed:', error);
80
80
  process.exitCode = 1;
81
- });
81
+ });
@@ -43,7 +43,7 @@ export function createTypeOrmOptions(
43
43
 
44
44
  default:
45
45
  throw new Error(
46
- `Banco "${databaseType}" não suportado pelo template TypeORM`,
46
+ `Database "${databaseType}" is not supported by the TypeORM template`,
47
47
  );
48
48
  }
49
- }
49
+ }
@@ -33,7 +33,7 @@ export class HealthController {
33
33
  @Get()
34
34
  @HealthCheck()
35
35
  // nestforge:feature:swagger
36
- @ApiOperation({ summary: 'Verifica a saúde da API (banco, memória, disco e demais dependências configuradas)' })
36
+ @ApiOperation({ summary: 'Checks API health (database, memory, disk, and other configured dependencies)' })
37
37
  // nestforge:feature:swagger:end
38
38
  check() {
39
39
  return this.health.check([
@@ -46,4 +46,4 @@ export class HealthController {
46
46
  () => this.disk.checkStorage('disk', { path: '/', thresholdPercent: 0.9 }),
47
47
  ]);
48
48
  }
49
- }
49
+ }
@@ -14,7 +14,7 @@ vi.mock('ioredis', () => ({
14
14
  })),
15
15
  }));
16
16
 
17
- // precisa ser importado depois do vi.mock, senão pega o ioredis real
17
+ // Must be imported after vi.mock; otherwise it loads the real ioredis package.
18
18
  const { RedisHealthIndicator } = await import('./redis-health.indicator');
19
19
 
20
20
  describe('RedisHealthIndicator', () => {
@@ -24,7 +24,7 @@ describe('RedisHealthIndicator', () => {
24
24
  disconnectMock.mockReset();
25
25
  });
26
26
 
27
- it('retorna status up quando o PING responde PONG', async () => {
27
+ it('returns an up status when PING returns PONG', async () => {
28
28
  connectMock.mockResolvedValue(undefined);
29
29
  pingMock.mockResolvedValue('PONG');
30
30
 
@@ -35,7 +35,7 @@ describe('RedisHealthIndicator', () => {
35
35
  expect(disconnectMock).toHaveBeenCalled();
36
36
  });
37
37
 
38
- it('lança HealthCheckError quando o PING não responde PONG', async () => {
38
+ it('throws HealthCheckError when PING does not return PONG', async () => {
39
39
  connectMock.mockResolvedValue(undefined);
40
40
  pingMock.mockResolvedValue('ALGO_INESPERADO');
41
41
 
@@ -44,11 +44,11 @@ describe('RedisHealthIndicator', () => {
44
44
  await expect(indicator.isHealthy('redis')).rejects.toBeInstanceOf(HealthCheckError);
45
45
  });
46
46
 
47
- it('lança HealthCheckError quando a conexão falha', async () => {
47
+ it('throws HealthCheckError when the connection fails', async () => {
48
48
  connectMock.mockRejectedValue(new Error('ECONNREFUSED'));
49
49
 
50
50
  const indicator = new RedisHealthIndicator();
51
51
 
52
52
  await expect(indicator.isHealthy('redis')).rejects.toBeInstanceOf(HealthCheckError);
53
53
  });
54
- });
54
+ });
@@ -18,16 +18,16 @@ export class RedisHealthIndicator {
18
18
  const response = await client.ping();
19
19
 
20
20
  if (response !== 'PONG') {
21
- throw new Error('Resposta inesperada do Redis');
21
+ throw new Error('Unexpected Redis response');
22
22
  }
23
23
 
24
24
  return { [key]: { status: 'up' } };
25
25
  } catch (error) {
26
- throw new HealthCheckError('Redis indisponível', {
26
+ throw new HealthCheckError('Redis unavailable', {
27
27
  [key]: { status: 'down', message: (error as Error).message },
28
28
  });
29
29
  } finally {
30
30
  client.disconnect();
31
31
  }
32
32
  }
33
- }
33
+ }
@@ -9,7 +9,7 @@ import { DataSource } from 'typeorm';
9
9
  import { TypeOrmHealthIndicator } from './typeorm-health.indicator';
10
10
 
11
11
  describe('TypeOrmHealthIndicator', () => {
12
- it('retorna status up quando a query executa com sucesso', async () => {
12
+ it('returns an up status when the query succeeds', async () => {
13
13
  const dataSource = {
14
14
  query: vi.fn().mockResolvedValue([{ result: 1 }]),
15
15
  };
@@ -29,12 +29,12 @@ describe('TypeOrmHealthIndicator', () => {
29
29
  });
30
30
  });
31
31
 
32
- it('lança HealthCheckError quando a query falha', async () => {
32
+ it('throws HealthCheckError when the query fails', async () => {
33
33
  const dataSource = {
34
34
  query: vi
35
35
  .fn()
36
36
  .mockRejectedValue(
37
- new Error('conexão recusada'),
37
+ new Error('connection refused'),
38
38
  ),
39
39
  };
40
40
 
@@ -46,4 +46,4 @@ describe('TypeOrmHealthIndicator', () => {
46
46
  indicator.isHealthy('database'),
47
47
  ).rejects.toBeInstanceOf(HealthCheckError);
48
48
  });
49
- });
49
+ });