nestforge-generator 0.3.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 (135) hide show
  1. package/README.md +13 -1
  2. package/README.pt-BR.md +13 -1
  3. package/dist/features/auth-strategy.js +7 -7
  4. package/dist/features/dependencies.js +3 -3
  5. package/dist/features/language.js +1 -1
  6. package/dist/features/language.js.map +1 -1
  7. package/dist/features/markers.js +11 -11
  8. package/dist/features/markers.js.map +1 -1
  9. package/dist/generator.js +7 -1
  10. package/dist/generator.js.map +1 -1
  11. package/dist/project-name.js +50 -0
  12. package/dist/project-name.js.map +1 -0
  13. package/dist/prompts.js +12 -10
  14. package/dist/prompts.js.map +1 -1
  15. package/package.json +1 -1
  16. package/templates/drizzle/drizzle.config.ts +2 -2
  17. package/templates/drizzle/src/auth/auth.controller.ts +35 -35
  18. package/templates/drizzle/src/auth/auth.service.spec.ts +4 -4
  19. package/templates/drizzle/src/auth/auth.service.ts +9 -9
  20. package/templates/drizzle/src/auth/dto/forgot-password.dto.ts +2 -2
  21. package/templates/drizzle/src/auth/dto/login.dto.ts +3 -3
  22. package/templates/drizzle/src/auth/dto/register.dto.ts +4 -4
  23. package/templates/drizzle/src/auth/dto/reset-password.dto.ts +3 -3
  24. package/templates/drizzle/src/auth/guards/github-auth.guard.ts +2 -2
  25. package/templates/drizzle/src/auth/guards/google-auth.guard.ts +2 -2
  26. package/templates/drizzle/src/auth/guards/session-auth.guard.spec.ts +4 -4
  27. package/templates/drizzle/src/auth/guards/session-auth.guard.ts +2 -2
  28. package/templates/drizzle/src/auth/session.service.spec.ts +5 -5
  29. package/templates/drizzle/src/auth/session.service.ts +5 -5
  30. package/templates/drizzle/src/auth/strategies/github.strategy.ts +3 -3
  31. package/templates/drizzle/src/auth/token.service.ts +3 -3
  32. package/templates/drizzle/src/common/filters/http-exception.filter.ts +1 -1
  33. package/templates/drizzle/src/common/guards/permissions.guard.spec.ts +7 -7
  34. package/templates/drizzle/src/common/guards/roles.guard.spec.ts +4 -4
  35. package/templates/drizzle/src/common/middleware/csrf.middleware.spec.ts +5 -5
  36. package/templates/drizzle/src/common/middleware/csrf.middleware.ts +2 -2
  37. package/templates/drizzle/src/common/utils/avatar-storage.util.ts +2 -2
  38. package/templates/drizzle/src/config/env.validation.ts +1 -1
  39. package/templates/drizzle/src/database/seed.ts +4 -4
  40. package/templates/drizzle/src/health/health.controller.ts +2 -2
  41. package/templates/drizzle/src/health/indicators/drizzle-health.indicator.spec.ts +5 -5
  42. package/templates/drizzle/src/health/indicators/drizzle-health.indicator.ts +2 -2
  43. package/templates/drizzle/src/health/indicators/redis-health.indicator.spec.ts +5 -5
  44. package/templates/drizzle/src/health/indicators/redis-health.indicator.ts +3 -3
  45. package/templates/drizzle/src/mail/mail.processor.ts +4 -4
  46. package/templates/drizzle/src/mail/templates/email-templates.ts +7 -7
  47. package/templates/drizzle/src/metrics/metrics.service.ts +3 -3
  48. package/templates/drizzle/src/users/dto/create-user.dto.ts +5 -5
  49. package/templates/drizzle/src/users/dto/find-users-query.dto.ts +3 -3
  50. package/templates/drizzle/src/users/users.controller.ts +21 -21
  51. package/templates/drizzle/src/users/users.service.spec.ts +12 -12
  52. package/templates/drizzle/src/users/users.service.ts +4 -4
  53. package/templates/drizzle/test/auth.e2e-spec.ts +8 -8
  54. package/templates/drizzle/test/session-auth.e2e-spec.ts +9 -9
  55. package/templates/drizzle/test/users.e2e-spec.ts +18 -18
  56. package/templates/prisma/prisma/seed.ts +46 -46
  57. package/templates/prisma/src/auth/auth.controller.ts +35 -35
  58. package/templates/prisma/src/auth/auth.service.spec.ts +3 -3
  59. package/templates/prisma/src/auth/auth.service.ts +11 -11
  60. package/templates/prisma/src/auth/dto/forgot-password.dto.ts +2 -2
  61. package/templates/prisma/src/auth/dto/login.dto.ts +3 -3
  62. package/templates/prisma/src/auth/dto/register.dto.ts +4 -4
  63. package/templates/prisma/src/auth/dto/reset-password.dto.ts +3 -3
  64. package/templates/prisma/src/auth/guards/github-auth.guard.ts +10 -10
  65. package/templates/prisma/src/auth/guards/google-auth.guard.ts +10 -10
  66. package/templates/prisma/src/auth/guards/session-auth.guard.spec.ts +4 -4
  67. package/templates/prisma/src/auth/guards/session-auth.guard.ts +2 -2
  68. package/templates/prisma/src/auth/session.service.spec.ts +5 -5
  69. package/templates/prisma/src/auth/session.service.ts +5 -5
  70. package/templates/prisma/src/auth/strategies/github.strategy.ts +3 -3
  71. package/templates/prisma/src/auth/token.service.ts +2 -2
  72. package/templates/prisma/src/common/filters/http-exception.filter.ts +34 -34
  73. package/templates/prisma/src/common/guards/permissions.guard.spec.ts +7 -7
  74. package/templates/prisma/src/common/guards/roles.guard.spec.ts +4 -4
  75. package/templates/prisma/src/common/middleware/csrf.middleware.spec.ts +5 -5
  76. package/templates/prisma/src/common/middleware/csrf.middleware.ts +2 -2
  77. package/templates/prisma/src/common/utils/avatar-storage.util.ts +32 -32
  78. package/templates/prisma/src/config/env.validation.ts +1 -1
  79. package/templates/prisma/src/health/health.controller.ts +2 -2
  80. package/templates/prisma/src/health/indicators/prisma-health.indicator.spec.ts +4 -4
  81. package/templates/prisma/src/health/indicators/prisma-health.indicator.ts +1 -1
  82. package/templates/prisma/src/health/indicators/redis-health.indicator.spec.ts +5 -5
  83. package/templates/prisma/src/health/indicators/redis-health.indicator.ts +3 -3
  84. package/templates/prisma/src/mail/mail.processor.ts +4 -4
  85. package/templates/prisma/src/mail/templates/email-templates.ts +7 -7
  86. package/templates/prisma/src/metrics/metrics.service.ts +34 -34
  87. package/templates/prisma/src/users/dto/create-user.dto.ts +12 -12
  88. package/templates/prisma/src/users/dto/find-users-query.dto.ts +12 -12
  89. package/templates/prisma/src/users/users.controller.ts +21 -21
  90. package/templates/prisma/src/users/users.service.spec.ts +11 -11
  91. package/templates/prisma/src/users/users.service.ts +4 -4
  92. package/templates/prisma/test/auth.e2e-spec.ts +8 -8
  93. package/templates/prisma/test/session-auth.e2e-spec.ts +9 -9
  94. package/templates/prisma/test/users.e2e-spec.ts +14 -14
  95. package/templates/typeorm/src/auth/auth.controller.ts +35 -35
  96. package/templates/typeorm/src/auth/auth.service.spec.ts +4 -4
  97. package/templates/typeorm/src/auth/auth.service.ts +9 -9
  98. package/templates/typeorm/src/auth/dto/forgot-password.dto.ts +2 -2
  99. package/templates/typeorm/src/auth/dto/login.dto.ts +3 -3
  100. package/templates/typeorm/src/auth/dto/register.dto.ts +4 -4
  101. package/templates/typeorm/src/auth/dto/reset-password.dto.ts +3 -3
  102. package/templates/typeorm/src/auth/guards/github-auth.guard.ts +2 -2
  103. package/templates/typeorm/src/auth/guards/google-auth.guard.ts +2 -2
  104. package/templates/typeorm/src/auth/guards/session-auth.guard.spec.ts +4 -4
  105. package/templates/typeorm/src/auth/guards/session-auth.guard.ts +2 -2
  106. package/templates/typeorm/src/auth/session.service.spec.ts +5 -5
  107. package/templates/typeorm/src/auth/session.service.ts +5 -5
  108. package/templates/typeorm/src/auth/strategies/github.strategy.ts +3 -3
  109. package/templates/typeorm/src/auth/token.service.ts +3 -3
  110. package/templates/typeorm/src/common/filters/http-exception.filter.ts +1 -1
  111. package/templates/typeorm/src/common/guards/permissions.guard.spec.ts +7 -7
  112. package/templates/typeorm/src/common/guards/roles.guard.spec.ts +4 -4
  113. package/templates/typeorm/src/common/middleware/csrf.middleware.spec.ts +5 -5
  114. package/templates/typeorm/src/common/middleware/csrf.middleware.ts +2 -2
  115. package/templates/typeorm/src/common/utils/avatar-storage.util.ts +2 -2
  116. package/templates/typeorm/src/config/env.validation.ts +1 -1
  117. package/templates/typeorm/src/database/data-source.ts +2 -2
  118. package/templates/typeorm/src/database/seed.ts +3 -3
  119. package/templates/typeorm/src/database/typeorm-options.ts +2 -2
  120. package/templates/typeorm/src/health/health.controller.ts +2 -2
  121. package/templates/typeorm/src/health/indicators/redis-health.indicator.spec.ts +5 -5
  122. package/templates/typeorm/src/health/indicators/redis-health.indicator.ts +3 -3
  123. package/templates/typeorm/src/health/indicators/typeorm-health.indicator.spec.ts +4 -4
  124. package/templates/typeorm/src/health/indicators/typeorm-health.indicator.ts +2 -2
  125. package/templates/typeorm/src/mail/mail.processor.ts +4 -4
  126. package/templates/typeorm/src/mail/templates/email-templates.ts +7 -7
  127. package/templates/typeorm/src/metrics/metrics.service.ts +3 -3
  128. package/templates/typeorm/src/users/dto/create-user.dto.ts +5 -5
  129. package/templates/typeorm/src/users/dto/find-users-query.dto.ts +3 -3
  130. package/templates/typeorm/src/users/users.controller.ts +21 -21
  131. package/templates/typeorm/src/users/users.service.spec.ts +12 -12
  132. package/templates/typeorm/src/users/users.service.ts +4 -4
  133. package/templates/typeorm/test/auth.e2e-spec.ts +8 -8
  134. package/templates/typeorm/test/session-auth.e2e-spec.ts +9 -9
  135. package/templates/typeorm/test/users.e2e-spec.ts +18 -18
@@ -1,34 +1,34 @@
1
- import {
2
- ArgumentsHost,
3
- Catch,
4
- ExceptionFilter,
5
- HttpException,
6
- HttpStatus,
7
- } from '@nestjs/common';
8
- import { Request, Response } from 'express';
9
-
10
- @Catch()
11
- export class HttpExceptionFilter implements ExceptionFilter {
12
- catch(exception: unknown, host: ArgumentsHost) {
13
- const ctx = host.switchToHttp();
14
- const response = ctx.getResponse<Response>();
15
- const request = ctx.getRequest<Request>();
16
-
17
- const status =
18
- exception instanceof HttpException
19
- ? exception.getStatus()
20
- : HttpStatus.INTERNAL_SERVER_ERROR;
21
-
22
- const message =
23
- exception instanceof HttpException
24
- ? exception.getResponse()
25
- : 'Erro interno do servidor';
26
-
27
- response.status(status).json({
28
- statusCode: status,
29
- path: request.url,
30
- timestamp: new Date().toISOString(),
31
- message,
32
- });
33
- }
34
- }
1
+ import {
2
+ ArgumentsHost,
3
+ Catch,
4
+ ExceptionFilter,
5
+ HttpException,
6
+ HttpStatus,
7
+ } from '@nestjs/common';
8
+ import { Request, Response } from 'express';
9
+
10
+ @Catch()
11
+ export class HttpExceptionFilter implements ExceptionFilter {
12
+ catch(exception: unknown, host: ArgumentsHost) {
13
+ const ctx = host.switchToHttp();
14
+ const response = ctx.getResponse<Response>();
15
+ const request = ctx.getRequest<Request>();
16
+
17
+ const status =
18
+ exception instanceof HttpException
19
+ ? exception.getStatus()
20
+ : HttpStatus.INTERNAL_SERVER_ERROR;
21
+
22
+ const message =
23
+ exception instanceof HttpException
24
+ ? exception.getResponse()
25
+ : 'Internal server error';
26
+
27
+ response.status(status).json({
28
+ statusCode: status,
29
+ path: request.url,
30
+ timestamp: new Date().toISOString(),
31
+ message,
32
+ });
33
+ }
34
+ }
@@ -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
+ }
@@ -1,32 +1,32 @@
1
- import { BadRequestException } from '@nestjs/common';
2
- import { randomUUID } from 'crypto';
3
- import { diskStorage } from 'multer';
4
- import { extname } from 'path';
5
-
6
- const ALLOWED_MIME_TYPES = ['image/png', 'image/jpeg', 'image/webp'];
7
-
8
- export const avatarStorage = diskStorage({
9
- destination: './uploads/avatars',
10
- filename: (_req, file, callback) => {
11
- const uniqueName = `${randomUUID()}${extname(file.originalname)}`;
12
- callback(null, uniqueName);
13
- },
14
- });
15
-
16
- export function avatarFileFilter(
17
- _req: unknown,
18
- file: Express.Multer.File,
19
- callback: (error: Error | null, acceptFile: boolean) => void,
20
- ) {
21
- if (!ALLOWED_MIME_TYPES.includes(file.mimetype)) {
22
- callback(
23
- new BadRequestException('Formato de imagem não suportado (use PNG, JPEG ou WEBP)'),
24
- false,
25
- );
26
- return;
27
- }
28
-
29
- callback(null, true);
30
- }
31
-
32
- export const AVATAR_MAX_SIZE_BYTES = 2 * 1024 * 1024; // 2MB
1
+ import { BadRequestException } from '@nestjs/common';
2
+ import { randomUUID } from 'crypto';
3
+ import { diskStorage } from 'multer';
4
+ import { extname } from 'path';
5
+
6
+ const ALLOWED_MIME_TYPES = ['image/png', 'image/jpeg', 'image/webp'];
7
+
8
+ export const avatarStorage = diskStorage({
9
+ destination: './uploads/avatars',
10
+ filename: (_req, file, callback) => {
11
+ const uniqueName = `${randomUUID()}${extname(file.originalname)}`;
12
+ callback(null, uniqueName);
13
+ },
14
+ });
15
+
16
+ export function avatarFileFilter(
17
+ _req: unknown,
18
+ file: Express.Multer.File,
19
+ callback: (error: Error | null, acceptFile: boolean) => void,
20
+ ) {
21
+ if (!ALLOWED_MIME_TYPES.includes(file.mimetype)) {
22
+ callback(
23
+ new BadRequestException('Unsupported image format (use PNG, JPEG, or WEBP)'),
24
+ false,
25
+ );
26
+ return;
27
+ }
28
+
29
+ callback(null, true);
30
+ }
31
+
32
+ export const AVATAR_MAX_SIZE_BYTES = 2 * 1024 * 1024; // 2MB
@@ -42,7 +42,7 @@ export function validateEnv(config: Record<string, unknown>): EnvConfig {
42
42
  const issues = parsed.error.issues
43
43
  .map((issue) => `${issue.path.join('.')}: ${issue.message}`)
44
44
  .join('\n');
45
- throw new Error(`Variáveis de ambiente inválidas:\n${issues}`);
45
+ throw new Error(`Invalid environment variables:\n${issues}`);
46
46
  }
47
47
 
48
48
  return parsed.data;
@@ -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
+ }
@@ -4,7 +4,7 @@ import { PrismaHealthIndicator } from './prisma-health.indicator';
4
4
  import { PrismaService } from '../../database/prisma.service';
5
5
 
6
6
  describe('PrismaHealthIndicator', () => {
7
- it('retorna status up quando a query executa com sucesso', async () => {
7
+ it('returns an up status when the query succeeds', async () => {
8
8
  const prisma = {
9
9
  // nestforge:feature:database:relational
10
10
  $queryRaw: vi.fn().mockResolvedValue([{ '?column?': 1 }]),
@@ -23,13 +23,13 @@ describe('PrismaHealthIndicator', () => {
23
23
  // nestforge:feature:database:mongodb:end
24
24
  });
25
25
 
26
- it('lança HealthCheckError quando a query falha', async () => {
26
+ it('throws HealthCheckError when the query fails', async () => {
27
27
  const prisma = {
28
28
  // nestforge:feature:database:relational
29
- $queryRaw: vi.fn().mockRejectedValue(new Error('conexão recusada')),
29
+ $queryRaw: vi.fn().mockRejectedValue(new Error('connection refused')),
30
30
  // nestforge:feature:database:relational:end
31
31
  // nestforge:feature:database:mongodb
32
- $runCommandRaw: vi.fn().mockRejectedValue(new Error('conexão recusada')),
32
+ $runCommandRaw: vi.fn().mockRejectedValue(new Error('connection refused')),
33
33
  // nestforge:feature:database:mongodb:end
34
34
  };
35
35
  const indicator = new PrismaHealthIndicator(prisma as unknown as PrismaService);
@@ -16,7 +16,7 @@ export class PrismaHealthIndicator {
16
16
  // nestforge:feature:database:mongodb:end
17
17
  return { [key]: { status: 'up' } };
18
18
  } catch (error) {
19
- throw new HealthCheckError('Banco de dados indisponível', {
19
+ throw new HealthCheckError('Database unavailable', {
20
20
  [key]: { status: 'down', message: (error as Error).message },
21
21
  });
22
22
  }
@@ -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
+ }
@@ -27,13 +27,13 @@ export class MailProcessor extends WorkerHost {
27
27
 
28
28
  switch (job.name) {
29
29
  case MailJob.VerifyEmail:
30
- await this.send(to, 'Confirme seu e-mail', verifyEmailTemplate(name, token));
30
+ await this.send(to, 'Confirm your email', verifyEmailTemplate(name, token));
31
31
  break;
32
32
  case MailJob.ResetPassword:
33
- await this.send(to, 'Redefinição de senha', resetPasswordTemplate(name, token));
33
+ await this.send(to, 'Password reset', resetPasswordTemplate(name, token));
34
34
  break;
35
35
  default:
36
- this.logger.warn(`Job de e-mail desconhecido: ${job.name}`);
36
+ this.logger.warn(`Unknown email job: ${job.name}`);
37
37
  }
38
38
  }
39
39
 
@@ -45,4 +45,4 @@ export class MailProcessor extends WorkerHost {
45
45
  html,
46
46
  });
47
47
  }
48
- }
48
+ }
@@ -6,10 +6,10 @@ export function verifyEmailTemplate(name: string, token: string): string {
6
6
 
7
7
  return `
8
8
  <div style="font-family: sans-serif; line-height: 1.5;">
9
- <h2>Olá, ${name}!</h2>
10
- <p>Confirme seu e-mail clicando no link abaixo:</p>
9
+ <h2>Hello, ${name}!</h2>
10
+ <p>Confirm your email by clicking the link below:</p>
11
11
  <p><a href="${link}">${link}</a></p>
12
- <p>Esse link expira em 24 horas.</p>
12
+ <p>This link expires in 24 hours.</p>
13
13
  </div>
14
14
  `;
15
15
  }
@@ -19,10 +19,10 @@ export function resetPasswordTemplate(name: string, token: string): string {
19
19
 
20
20
  return `
21
21
  <div style="font-family: sans-serif; line-height: 1.5;">
22
- <h2>Olá, ${name}!</h2>
23
- <p>Recebemos um pedido para redefinir sua senha. Clique no link abaixo para continuar:</p>
22
+ <h2>Hello, ${name}!</h2>
23
+ <p>We received a request to reset your password. Click the link below to continue:</p>
24
24
  <p><a href="${link}">${link}</a></p>
25
- <p>Se você não pediu isso, pode ignorar este e-mail. O link expira em 1 hora.</p>
25
+ <p>If you did not request this, you can ignore this email. The link expires in 1 hour.</p>
26
26
  </div>
27
27
  `;
28
- }
28
+ }
@@ -1,34 +1,34 @@
1
- import { Injectable } from '@nestjs/common';
2
- import * as client from 'prom-client';
3
-
4
- @Injectable()
5
- export class MetricsService {
6
- readonly registry = new client.Registry();
7
-
8
- readonly httpRequestDuration = new client.Histogram({
9
- name: 'http_request_duration_seconds',
10
- help: 'Duração das requisições HTTP em segundos',
11
- labelNames: ['method', 'route', 'status_code'],
12
- buckets: [0.05, 0.1, 0.3, 0.5, 1, 2, 5],
13
- registers: [this.registry],
14
- });
15
-
16
- readonly httpRequestsTotal = new client.Counter({
17
- name: 'http_requests_total',
18
- help: 'Total de requisições HTTP recebidas',
19
- labelNames: ['method', 'route', 'status_code'],
20
- registers: [this.registry],
21
- });
22
-
23
- constructor() {
24
- client.collectDefaultMetrics({ register: this.registry });
25
- }
26
-
27
- async getMetrics(): Promise<string> {
28
- return this.registry.metrics();
29
- }
30
-
31
- getContentType(): string {
32
- return this.registry.contentType;
33
- }
34
- }
1
+ import { Injectable } from '@nestjs/common';
2
+ import * as client from 'prom-client';
3
+
4
+ @Injectable()
5
+ export class MetricsService {
6
+ readonly registry = new client.Registry();
7
+
8
+ readonly httpRequestDuration = new client.Histogram({
9
+ name: 'http_request_duration_seconds',
10
+ help: 'HTTP request duration in seconds',
11
+ labelNames: ['method', 'route', 'status_code'],
12
+ buckets: [0.05, 0.1, 0.3, 0.5, 1, 2, 5],
13
+ registers: [this.registry],
14
+ });
15
+
16
+ readonly httpRequestsTotal = new client.Counter({
17
+ name: 'http_requests_total',
18
+ help: 'Total number of HTTP requests received',
19
+ labelNames: ['method', 'route', 'status_code'],
20
+ registers: [this.registry],
21
+ });
22
+
23
+ constructor() {
24
+ client.collectDefaultMetrics({ register: this.registry });
25
+ }
26
+
27
+ async getMetrics(): Promise<string> {
28
+ return this.registry.metrics();
29
+ }
30
+
31
+ getContentType(): string {
32
+ return this.registry.contentType;
33
+ }
34
+ }
@@ -1,12 +1,12 @@
1
- import { z } from 'zod';
2
- import { createZodDto } from 'nestjs-zod';
3
- import { Role } from '@prisma/client';
4
-
5
- export const createUserSchema = z.object({
6
- name: z.string().min(2).describe('Nome completo'),
7
- email: z.string().email().describe('E-mail do usuário'),
8
- password: z.string().min(8).describe('Senha (mínimo 8 caracteres)'),
9
- role: z.nativeEnum(Role).optional().describe('Role do usuário (padrão: USER)'),
10
- });
11
-
12
- export class CreateUserDto extends createZodDto(createUserSchema) { }
1
+ import { z } from 'zod';
2
+ import { createZodDto } from 'nestjs-zod';
3
+ import { Role } from '@prisma/client';
4
+
5
+ export const createUserSchema = z.object({
6
+ name: z.string().min(2).describe('Full name'),
7
+ email: z.string().email().describe('User email'),
8
+ password: z.string().min(8).describe('Password (minimum 8 characters)'),
9
+ role: z.nativeEnum(Role).optional().describe('User role (default: USER)'),
10
+ });
11
+
12
+ export class CreateUserDto extends createZodDto(createUserSchema) { }
@@ -1,12 +1,12 @@
1
- import { z } from 'zod';
2
- import { createZodDto } from 'nestjs-zod';
3
- import { Role } from '@prisma/client';
4
-
5
- export const findUsersQuerySchema = z.object({
6
- page: z.coerce.number().int().min(1).default(1).describe('Página atual'),
7
- limit: z.coerce.number().int().min(1).max(100).default(10).describe('Itens por página (máx. 100)'),
8
- search: z.string().optional().describe('Busca por nome ou e-mail'),
9
- role: z.nativeEnum(Role).optional().describe('Filtra por role'),
10
- });
11
-
12
- export class FindUsersQueryDto extends createZodDto(findUsersQuerySchema) { }
1
+ import { z } from 'zod';
2
+ import { createZodDto } from 'nestjs-zod';
3
+ import { Role } from '@prisma/client';
4
+
5
+ export const findUsersQuerySchema = z.object({
6
+ page: z.coerce.number().int().min(1).default(1).describe('Current page'),
7
+ limit: z.coerce.number().int().min(1).max(100).default(10).describe('Items per page (maximum 100)'),
8
+ search: z.string().optional().describe('Busca por nome ou e-mail'),
9
+ role: z.nativeEnum(Role).optional().describe('Filtra por role'),
10
+ });
11
+
12
+ export class FindUsersQueryDto extends createZodDto(findUsersQuerySchema) { }