@urbansolv/create-nestjs-app 1.0.2 → 1.2.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.
- package/dist/templates/nestjs-app/.editorconfig +12 -12
- package/dist/templates/nestjs-app/.env.example +24 -24
- package/dist/templates/nestjs-app/.eslintrc.js +25 -25
- package/dist/templates/nestjs-app/package.json +40 -30
- package/dist/templates/nestjs-app/prisma/schema.prisma +79 -79
- package/dist/templates/nestjs-app/prisma/seed.ts +153 -154
- package/dist/templates/nestjs-app/src/app.module.ts +68 -68
- package/dist/templates/nestjs-app/src/common/constants/permissions.constant.ts +27 -27
- package/dist/templates/nestjs-app/src/common/decorators/api-response.decorator.ts +44 -44
- package/dist/templates/nestjs-app/src/common/decorators/get-user.decorator.ts +11 -11
- package/dist/templates/nestjs-app/src/common/decorators/permissions.decorator.ts +5 -5
- package/dist/templates/nestjs-app/src/common/decorators/public.decorator.ts +4 -4
- package/dist/templates/nestjs-app/src/common/dto/api-response.dto.ts +21 -21
- package/dist/templates/nestjs-app/src/common/dto/pagination.dto.ts +33 -33
- package/dist/templates/nestjs-app/src/common/filters/http-exception.filter.ts +56 -56
- package/dist/templates/nestjs-app/src/common/guards/jwt-auth.guard.ts +32 -32
- package/dist/templates/nestjs-app/src/common/guards/permissions.guard.ts +53 -53
- package/dist/templates/nestjs-app/src/common/interceptors/logging.interceptor.ts +37 -37
- package/dist/templates/nestjs-app/src/common/interceptors/transform.interceptor.ts +55 -55
- package/dist/templates/nestjs-app/src/common/prisma/prisma.module.ts +9 -9
- package/dist/templates/nestjs-app/src/common/prisma/prisma.service.ts +46 -46
- package/dist/templates/nestjs-app/src/common/utils/password.util.ts +13 -13
- package/dist/templates/nestjs-app/src/config/app.config.ts +10 -10
- package/dist/templates/nestjs-app/src/config/database.config.ts +5 -5
- package/dist/templates/nestjs-app/src/config/env.validation.ts +30 -30
- package/dist/templates/nestjs-app/src/config/jwt.config.ts +8 -8
- package/dist/templates/nestjs-app/src/config/swagger.config.ts +6 -6
- package/dist/templates/nestjs-app/src/main.ts +94 -91
- package/dist/templates/nestjs-app/src/modules/auth/auth.module.ts +28 -27
- package/dist/templates/nestjs-app/src/modules/auth/auth.service.ts +180 -54
- package/dist/templates/nestjs-app/src/modules/auth/controllers/v1/auth.controller.ts +33 -33
- package/dist/templates/nestjs-app/src/modules/auth/{dto → core/dto}/auth-response.dto.ts +19 -19
- package/dist/templates/nestjs-app/src/modules/auth/core/dto/login-response.dto.ts +10 -0
- package/dist/templates/nestjs-app/src/modules/auth/{dto → core/dto}/login.dto.ts +15 -15
- package/dist/templates/nestjs-app/src/modules/auth/{dto → core/dto}/register.dto.ts +30 -30
- package/dist/templates/nestjs-app/src/modules/auth/{interfaces → core/interfaces}/jwt-payload.interface.ts +7 -7
- package/dist/templates/nestjs-app/src/modules/auth/{strategies → core/strategies}/jwt.strategy.ts +54 -54
- package/dist/templates/nestjs-app/src/modules/health/health.controller.ts +29 -29
- package/dist/templates/nestjs-app/src/modules/health/health.module.ts +7 -7
- package/dist/templates/nestjs-app/src/modules/users/controllers/v1/users.controller.ts +118 -114
- package/dist/templates/nestjs-app/src/modules/users/core/dto/change-position.dto.ts +9 -9
- package/dist/templates/nestjs-app/src/modules/users/core/dto/create-user.dto.ts +35 -35
- package/dist/templates/nestjs-app/src/modules/users/core/dto/manage-permissions.dto.ts +13 -13
- package/dist/templates/nestjs-app/src/modules/users/core/dto/update-user.dto.ts +30 -30
- package/dist/templates/nestjs-app/src/modules/users/core/dto/user-query.dto.ts +22 -22
- package/dist/templates/nestjs-app/src/modules/users/core/dto/user-response.dto.ts +32 -0
- package/dist/templates/nestjs-app/src/modules/users/core/entities/user.entity.ts +45 -45
- package/dist/templates/nestjs-app/src/modules/users/core/helpers/user-transform.helper.ts +31 -31
- package/dist/templates/nestjs-app/src/modules/users/users.module.ts +10 -10
- package/dist/templates/nestjs-app/src/modules/users/users.service.ts +340 -340
- package/package.json +2 -2
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
|
2
|
-
|
|
3
|
-
export class ApiResponseDto<T = any> {
|
|
4
|
-
@ApiProperty({ example: true })
|
|
5
|
-
success: boolean;
|
|
6
|
-
|
|
7
|
-
@ApiProperty({ example: 'Operation successful' })
|
|
8
|
-
message: string;
|
|
9
|
-
|
|
10
|
-
@ApiPropertyOptional()
|
|
11
|
-
data?: T;
|
|
12
|
-
|
|
13
|
-
@ApiPropertyOptional()
|
|
14
|
-
errors?: any;
|
|
15
|
-
|
|
16
|
-
@ApiProperty({ example: '2026-01-01T00:00:00.000Z' })
|
|
17
|
-
timestamp: string;
|
|
18
|
-
|
|
19
|
-
@ApiProperty({ example: '/api/v1/users' })
|
|
20
|
-
path: string;
|
|
21
|
-
}
|
|
1
|
+
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
|
2
|
+
|
|
3
|
+
export class ApiResponseDto<T = any> {
|
|
4
|
+
@ApiProperty({ example: true })
|
|
5
|
+
success: boolean;
|
|
6
|
+
|
|
7
|
+
@ApiProperty({ example: 'Operation successful' })
|
|
8
|
+
message: string;
|
|
9
|
+
|
|
10
|
+
@ApiPropertyOptional()
|
|
11
|
+
data?: T;
|
|
12
|
+
|
|
13
|
+
@ApiPropertyOptional()
|
|
14
|
+
errors?: any;
|
|
15
|
+
|
|
16
|
+
@ApiProperty({ example: '2026-01-01T00:00:00.000Z' })
|
|
17
|
+
timestamp: string;
|
|
18
|
+
|
|
19
|
+
@ApiProperty({ example: '/api/v1/users' })
|
|
20
|
+
path: string;
|
|
21
|
+
}
|
|
@@ -1,33 +1,33 @@
|
|
|
1
|
-
import { ApiPropertyOptional } from '@nestjs/swagger';
|
|
2
|
-
import { Type } from 'class-transformer';
|
|
3
|
-
import { IsOptional, IsInt, Min, Max } from 'class-validator';
|
|
4
|
-
|
|
5
|
-
export class PaginationDto {
|
|
6
|
-
@ApiPropertyOptional({ minimum: 1, default: 1 })
|
|
7
|
-
@Type(() => Number)
|
|
8
|
-
@IsInt()
|
|
9
|
-
@Min(1)
|
|
10
|
-
@IsOptional()
|
|
11
|
-
page?: number = 1;
|
|
12
|
-
|
|
13
|
-
@ApiPropertyOptional({ minimum: 1, maximum: 100, default: 10 })
|
|
14
|
-
@Type(() => Number)
|
|
15
|
-
@IsInt()
|
|
16
|
-
@Min(1)
|
|
17
|
-
@Max(100)
|
|
18
|
-
@IsOptional()
|
|
19
|
-
limit?: number = 10;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export class PaginatedResponseDto<T> {
|
|
23
|
-
@ApiPropertyOptional()
|
|
24
|
-
data: T[];
|
|
25
|
-
|
|
26
|
-
@ApiPropertyOptional()
|
|
27
|
-
meta: {
|
|
28
|
-
total: number;
|
|
29
|
-
page: number;
|
|
30
|
-
limit: number;
|
|
31
|
-
totalPages: number;
|
|
32
|
-
};
|
|
33
|
-
}
|
|
1
|
+
import { ApiPropertyOptional } from '@nestjs/swagger';
|
|
2
|
+
import { Type } from 'class-transformer';
|
|
3
|
+
import { IsOptional, IsInt, Min, Max } from 'class-validator';
|
|
4
|
+
|
|
5
|
+
export class PaginationDto {
|
|
6
|
+
@ApiPropertyOptional({ minimum: 1, default: 1 })
|
|
7
|
+
@Type(() => Number)
|
|
8
|
+
@IsInt()
|
|
9
|
+
@Min(1)
|
|
10
|
+
@IsOptional()
|
|
11
|
+
page?: number = 1;
|
|
12
|
+
|
|
13
|
+
@ApiPropertyOptional({ minimum: 1, maximum: 100, default: 10 })
|
|
14
|
+
@Type(() => Number)
|
|
15
|
+
@IsInt()
|
|
16
|
+
@Min(1)
|
|
17
|
+
@Max(100)
|
|
18
|
+
@IsOptional()
|
|
19
|
+
limit?: number = 10;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export class PaginatedResponseDto<T> {
|
|
23
|
+
@ApiPropertyOptional()
|
|
24
|
+
data: T[];
|
|
25
|
+
|
|
26
|
+
@ApiPropertyOptional()
|
|
27
|
+
meta: {
|
|
28
|
+
total: number;
|
|
29
|
+
page: number;
|
|
30
|
+
limit: number;
|
|
31
|
+
totalPages: number;
|
|
32
|
+
};
|
|
33
|
+
}
|
|
@@ -1,56 +1,56 @@
|
|
|
1
|
-
import {
|
|
2
|
-
ExceptionFilter,
|
|
3
|
-
Catch,
|
|
4
|
-
ArgumentsHost,
|
|
5
|
-
HttpException,
|
|
6
|
-
HttpStatus,
|
|
7
|
-
Logger,
|
|
8
|
-
} from '@nestjs/common';
|
|
9
|
-
import { Response } from 'express';
|
|
10
|
-
import { ApiResponseDto } from '@common/dto/api-response.dto';
|
|
11
|
-
|
|
12
|
-
@Catch()
|
|
13
|
-
export class HttpExceptionFilter implements ExceptionFilter {
|
|
14
|
-
private readonly logger = new Logger(HttpExceptionFilter.name);
|
|
15
|
-
|
|
16
|
-
catch(exception: unknown, host: ArgumentsHost) {
|
|
17
|
-
const ctx = host.switchToHttp();
|
|
18
|
-
const response = ctx.getResponse<Response>();
|
|
19
|
-
const request = ctx.getRequest<Request>();
|
|
20
|
-
|
|
21
|
-
let status = HttpStatus.INTERNAL_SERVER_ERROR;
|
|
22
|
-
let message = 'Internal server error';
|
|
23
|
-
let errors: any = null;
|
|
24
|
-
|
|
25
|
-
if (exception instanceof HttpException) {
|
|
26
|
-
status = exception.getStatus();
|
|
27
|
-
const exceptionResponse = exception.getResponse();
|
|
28
|
-
|
|
29
|
-
if (typeof exceptionResponse === 'object' && exceptionResponse !== null) {
|
|
30
|
-
message = (exceptionResponse as any).message || exception.message;
|
|
31
|
-
errors = (exceptionResponse as any).errors || null;
|
|
32
|
-
} else {
|
|
33
|
-
message = exceptionResponse as string;
|
|
34
|
-
}
|
|
35
|
-
} else if (exception instanceof Error) {
|
|
36
|
-
message = exception.message;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// Log error for monitoring
|
|
40
|
-
this.logger.error(
|
|
41
|
-
`${request.method} ${request.url} - Status: ${status} - Message: ${message}`,
|
|
42
|
-
exception instanceof Error ? exception.stack : undefined,
|
|
43
|
-
);
|
|
44
|
-
|
|
45
|
-
const errorResponse: ApiResponseDto<null> = {
|
|
46
|
-
success: false,
|
|
47
|
-
message,
|
|
48
|
-
data: null,
|
|
49
|
-
errors,
|
|
50
|
-
timestamp: new Date().toISOString(),
|
|
51
|
-
path: request.url,
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
response.status(status).json(errorResponse);
|
|
55
|
-
}
|
|
56
|
-
}
|
|
1
|
+
import {
|
|
2
|
+
ExceptionFilter,
|
|
3
|
+
Catch,
|
|
4
|
+
ArgumentsHost,
|
|
5
|
+
HttpException,
|
|
6
|
+
HttpStatus,
|
|
7
|
+
Logger,
|
|
8
|
+
} from '@nestjs/common';
|
|
9
|
+
import { Response } from 'express';
|
|
10
|
+
import { ApiResponseDto } from '@common/dto/api-response.dto';
|
|
11
|
+
|
|
12
|
+
@Catch()
|
|
13
|
+
export class HttpExceptionFilter implements ExceptionFilter {
|
|
14
|
+
private readonly logger = new Logger(HttpExceptionFilter.name);
|
|
15
|
+
|
|
16
|
+
catch(exception: unknown, host: ArgumentsHost) {
|
|
17
|
+
const ctx = host.switchToHttp();
|
|
18
|
+
const response = ctx.getResponse<Response>();
|
|
19
|
+
const request = ctx.getRequest<Request>();
|
|
20
|
+
|
|
21
|
+
let status = HttpStatus.INTERNAL_SERVER_ERROR;
|
|
22
|
+
let message = 'Internal server error';
|
|
23
|
+
let errors: any = null;
|
|
24
|
+
|
|
25
|
+
if (exception instanceof HttpException) {
|
|
26
|
+
status = exception.getStatus();
|
|
27
|
+
const exceptionResponse = exception.getResponse();
|
|
28
|
+
|
|
29
|
+
if (typeof exceptionResponse === 'object' && exceptionResponse !== null) {
|
|
30
|
+
message = (exceptionResponse as any).message || exception.message;
|
|
31
|
+
errors = (exceptionResponse as any).errors || null;
|
|
32
|
+
} else {
|
|
33
|
+
message = exceptionResponse as string;
|
|
34
|
+
}
|
|
35
|
+
} else if (exception instanceof Error) {
|
|
36
|
+
message = exception.message;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Log error for monitoring
|
|
40
|
+
this.logger.error(
|
|
41
|
+
`${request.method} ${request.url} - Status: ${status} - Message: ${message}`,
|
|
42
|
+
exception instanceof Error ? exception.stack : undefined,
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
const errorResponse: ApiResponseDto<null> = {
|
|
46
|
+
success: false,
|
|
47
|
+
message,
|
|
48
|
+
data: null,
|
|
49
|
+
errors,
|
|
50
|
+
timestamp: new Date().toISOString(),
|
|
51
|
+
path: request.url,
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
response.status(status).json(errorResponse);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -1,32 +1,32 @@
|
|
|
1
|
-
import { ExecutionContext, Injectable, UnauthorizedException } from '@nestjs/common';
|
|
2
|
-
import { Reflector } from '@nestjs/core';
|
|
3
|
-
import { AuthGuard } from '@nestjs/passport';
|
|
4
|
-
import { IS_PUBLIC_KEY } from '@common/decorators/public.decorator';
|
|
5
|
-
import { Observable } from 'rxjs';
|
|
6
|
-
|
|
7
|
-
@Injectable()
|
|
8
|
-
export class JwtAuthGuard extends AuthGuard('jwt') {
|
|
9
|
-
constructor(private reflector: Reflector) {
|
|
10
|
-
super();
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
canActivate(context: ExecutionContext): boolean | Promise<boolean> | Observable<boolean> {
|
|
14
|
-
const isPublic = this.reflector.getAllAndOverride<boolean>(IS_PUBLIC_KEY, [
|
|
15
|
-
context.getHandler(),
|
|
16
|
-
context.getClass(),
|
|
17
|
-
]);
|
|
18
|
-
|
|
19
|
-
if (isPublic) {
|
|
20
|
-
return true;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
return super.canActivate(context);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
handleRequest(err: any, user: any, info: any) {
|
|
27
|
-
if (err || !user) {
|
|
28
|
-
throw err || new UnauthorizedException('Invalid or expired token');
|
|
29
|
-
}
|
|
30
|
-
return user;
|
|
31
|
-
}
|
|
32
|
-
}
|
|
1
|
+
import { ExecutionContext, Injectable, UnauthorizedException } from '@nestjs/common';
|
|
2
|
+
import { Reflector } from '@nestjs/core';
|
|
3
|
+
import { AuthGuard } from '@nestjs/passport';
|
|
4
|
+
import { IS_PUBLIC_KEY } from '@common/decorators/public.decorator';
|
|
5
|
+
import { Observable } from 'rxjs';
|
|
6
|
+
|
|
7
|
+
@Injectable()
|
|
8
|
+
export class JwtAuthGuard extends AuthGuard('jwt') {
|
|
9
|
+
constructor(private reflector: Reflector) {
|
|
10
|
+
super();
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
canActivate(context: ExecutionContext): boolean | Promise<boolean> | Observable<boolean> {
|
|
14
|
+
const isPublic = this.reflector.getAllAndOverride<boolean>(IS_PUBLIC_KEY, [
|
|
15
|
+
context.getHandler(),
|
|
16
|
+
context.getClass(),
|
|
17
|
+
]);
|
|
18
|
+
|
|
19
|
+
if (isPublic) {
|
|
20
|
+
return true;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return super.canActivate(context);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
handleRequest(err: any, user: any, info: any) {
|
|
27
|
+
if (err || !user) {
|
|
28
|
+
throw err || new UnauthorizedException('Invalid or expired token');
|
|
29
|
+
}
|
|
30
|
+
return user;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -1,53 +1,53 @@
|
|
|
1
|
-
import { Injectable, CanActivate, ExecutionContext, ForbiddenException } from '@nestjs/common';
|
|
2
|
-
import { Reflector } from '@nestjs/core';
|
|
3
|
-
import { PERMISSIONS_KEY } from '@common/decorators/permissions.decorator';
|
|
4
|
-
import { IS_PUBLIC_KEY } from '@common/decorators/public.decorator';
|
|
5
|
-
import { JwtPayload } from '@modules/auth/interfaces/jwt-payload.interface';
|
|
6
|
-
|
|
7
|
-
@Injectable()
|
|
8
|
-
export class PermissionsGuard implements CanActivate {
|
|
9
|
-
constructor(private reflector: Reflector) {}
|
|
10
|
-
|
|
11
|
-
canActivate(context: ExecutionContext): boolean {
|
|
12
|
-
// Check if route is public
|
|
13
|
-
const isPublic = this.reflector.getAllAndOverride<boolean>(IS_PUBLIC_KEY, [
|
|
14
|
-
context.getHandler(),
|
|
15
|
-
context.getClass(),
|
|
16
|
-
]);
|
|
17
|
-
|
|
18
|
-
if (isPublic) {
|
|
19
|
-
return true;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
// Get required permissions from decorator
|
|
23
|
-
const requiredPermissions = this.reflector.getAllAndOverride<string[]>(PERMISSIONS_KEY, [
|
|
24
|
-
context.getHandler(),
|
|
25
|
-
context.getClass(),
|
|
26
|
-
]);
|
|
27
|
-
|
|
28
|
-
if (!requiredPermissions || requiredPermissions.length === 0) {
|
|
29
|
-
return true;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
// Get user from request
|
|
33
|
-
const request = context.switchToHttp().getRequest();
|
|
34
|
-
const user: JwtPayload = request.user;
|
|
35
|
-
|
|
36
|
-
if (!user) {
|
|
37
|
-
throw new ForbiddenException('User not authenticated');
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// Check if user has required permissions
|
|
41
|
-
const hasPermission = requiredPermissions.every((permission) =>
|
|
42
|
-
user.permissions.includes(permission),
|
|
43
|
-
);
|
|
44
|
-
|
|
45
|
-
if (!hasPermission) {
|
|
46
|
-
throw new ForbiddenException(
|
|
47
|
-
`You do not have the required permissions: ${requiredPermissions.join(', ')}`,
|
|
48
|
-
);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
return true;
|
|
52
|
-
}
|
|
53
|
-
}
|
|
1
|
+
import { Injectable, CanActivate, ExecutionContext, ForbiddenException } from '@nestjs/common';
|
|
2
|
+
import { Reflector } from '@nestjs/core';
|
|
3
|
+
import { PERMISSIONS_KEY } from '@common/decorators/permissions.decorator';
|
|
4
|
+
import { IS_PUBLIC_KEY } from '@common/decorators/public.decorator';
|
|
5
|
+
import { JwtPayload } from '@modules/auth/core/interfaces/jwt-payload.interface';
|
|
6
|
+
|
|
7
|
+
@Injectable()
|
|
8
|
+
export class PermissionsGuard implements CanActivate {
|
|
9
|
+
constructor(private reflector: Reflector) {}
|
|
10
|
+
|
|
11
|
+
canActivate(context: ExecutionContext): boolean {
|
|
12
|
+
// Check if route is public
|
|
13
|
+
const isPublic = this.reflector.getAllAndOverride<boolean>(IS_PUBLIC_KEY, [
|
|
14
|
+
context.getHandler(),
|
|
15
|
+
context.getClass(),
|
|
16
|
+
]);
|
|
17
|
+
|
|
18
|
+
if (isPublic) {
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Get required permissions from decorator
|
|
23
|
+
const requiredPermissions = this.reflector.getAllAndOverride<string[]>(PERMISSIONS_KEY, [
|
|
24
|
+
context.getHandler(),
|
|
25
|
+
context.getClass(),
|
|
26
|
+
]);
|
|
27
|
+
|
|
28
|
+
if (!requiredPermissions || requiredPermissions.length === 0) {
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Get user from request
|
|
33
|
+
const request = context.switchToHttp().getRequest();
|
|
34
|
+
const user: JwtPayload = request.user;
|
|
35
|
+
|
|
36
|
+
if (!user) {
|
|
37
|
+
throw new ForbiddenException('User not authenticated');
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Check if user has required permissions
|
|
41
|
+
const hasPermission = requiredPermissions.every((permission) =>
|
|
42
|
+
user.permissions.includes(permission),
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
if (!hasPermission) {
|
|
46
|
+
throw new ForbiddenException(
|
|
47
|
+
`You do not have the required permissions: ${requiredPermissions.join(', ')}`,
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -1,37 +1,37 @@
|
|
|
1
|
-
import { Injectable, NestInterceptor, ExecutionContext, CallHandler, Logger } from '@nestjs/common';
|
|
2
|
-
import { Observable } from 'rxjs';
|
|
3
|
-
import { tap } from 'rxjs/operators';
|
|
4
|
-
|
|
5
|
-
@Injectable()
|
|
6
|
-
export class LoggingInterceptor implements NestInterceptor {
|
|
7
|
-
private readonly logger = new Logger('HTTP');
|
|
8
|
-
|
|
9
|
-
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
|
|
10
|
-
const request = context.switchToHttp().getRequest();
|
|
11
|
-
const { method, url, body, user } = request;
|
|
12
|
-
const now = Date.now();
|
|
13
|
-
|
|
14
|
-
const userId = user?.userId || 'anonymous';
|
|
15
|
-
|
|
16
|
-
this.logger.log(
|
|
17
|
-
`→ [${method}] ${url} - User: ${userId} ${
|
|
18
|
-
Object.keys(body || {}).length > 0 ? `- Body: ${JSON.stringify(body)}` : ''
|
|
19
|
-
}`,
|
|
20
|
-
);
|
|
21
|
-
|
|
22
|
-
return next.handle().pipe(
|
|
23
|
-
tap({
|
|
24
|
-
next: () => {
|
|
25
|
-
const responseTime = Date.now() - now;
|
|
26
|
-
this.logger.log(`← [${method}] ${url} - ${responseTime}ms - User: ${userId}`);
|
|
27
|
-
},
|
|
28
|
-
error: (error) => {
|
|
29
|
-
const responseTime = Date.now() - now;
|
|
30
|
-
this.logger.error(
|
|
31
|
-
`← [${method}] ${url} - ${responseTime}ms - User: ${userId} - Error: ${error.message}`,
|
|
32
|
-
);
|
|
33
|
-
},
|
|
34
|
-
}),
|
|
35
|
-
);
|
|
36
|
-
}
|
|
37
|
-
}
|
|
1
|
+
import { Injectable, NestInterceptor, ExecutionContext, CallHandler, Logger } from '@nestjs/common';
|
|
2
|
+
import { Observable } from 'rxjs';
|
|
3
|
+
import { tap } from 'rxjs/operators';
|
|
4
|
+
|
|
5
|
+
@Injectable()
|
|
6
|
+
export class LoggingInterceptor implements NestInterceptor {
|
|
7
|
+
private readonly logger = new Logger('HTTP');
|
|
8
|
+
|
|
9
|
+
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
|
|
10
|
+
const request = context.switchToHttp().getRequest();
|
|
11
|
+
const { method, url, body, user } = request;
|
|
12
|
+
const now = Date.now();
|
|
13
|
+
|
|
14
|
+
const userId = user?.userId || 'anonymous';
|
|
15
|
+
|
|
16
|
+
this.logger.log(
|
|
17
|
+
`→ [${method}] ${url} - User: ${userId} ${
|
|
18
|
+
Object.keys(body || {}).length > 0 ? `- Body: ${JSON.stringify(body)}` : ''
|
|
19
|
+
}`,
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
return next.handle().pipe(
|
|
23
|
+
tap({
|
|
24
|
+
next: () => {
|
|
25
|
+
const responseTime = Date.now() - now;
|
|
26
|
+
this.logger.log(`← [${method}] ${url} - ${responseTime}ms - User: ${userId}`);
|
|
27
|
+
},
|
|
28
|
+
error: (error) => {
|
|
29
|
+
const responseTime = Date.now() - now;
|
|
30
|
+
this.logger.error(
|
|
31
|
+
`← [${method}] ${url} - ${responseTime}ms - User: ${userId} - Error: ${error.message}`,
|
|
32
|
+
);
|
|
33
|
+
},
|
|
34
|
+
}),
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -1,55 +1,55 @@
|
|
|
1
|
-
import {
|
|
2
|
-
Injectable,
|
|
3
|
-
NestInterceptor,
|
|
4
|
-
ExecutionContext,
|
|
5
|
-
CallHandler,
|
|
6
|
-
HttpStatus,
|
|
7
|
-
} from '@nestjs/common';
|
|
8
|
-
import { Observable } from 'rxjs';
|
|
9
|
-
import { map } from 'rxjs/operators';
|
|
10
|
-
import { ApiResponseDto } from '@common/dto/api-response.dto';
|
|
11
|
-
|
|
12
|
-
@Injectable()
|
|
13
|
-
export class TransformInterceptor<T> implements NestInterceptor<T, ApiResponseDto<T>> {
|
|
14
|
-
intercept(context: ExecutionContext, next: CallHandler): Observable<ApiResponseDto<T>> {
|
|
15
|
-
const request = context.switchToHttp().getRequest();
|
|
16
|
-
const response = context.switchToHttp().getResponse();
|
|
17
|
-
|
|
18
|
-
return next.handle().pipe(
|
|
19
|
-
map((data) => {
|
|
20
|
-
// If data is already in ApiResponseDto format, return as is
|
|
21
|
-
if (data && typeof data === 'object' && 'success' in data && 'message' in data) {
|
|
22
|
-
return data;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
// Default success message based on HTTP method
|
|
26
|
-
let message = 'Operation successful';
|
|
27
|
-
|
|
28
|
-
switch (request.method) {
|
|
29
|
-
case 'POST':
|
|
30
|
-
message = 'Resource created successfully';
|
|
31
|
-
response.status(HttpStatus.CREATED);
|
|
32
|
-
break;
|
|
33
|
-
case 'PUT':
|
|
34
|
-
case 'PATCH':
|
|
35
|
-
message = 'Resource updated successfully';
|
|
36
|
-
break;
|
|
37
|
-
case 'DELETE':
|
|
38
|
-
message = 'Resource deleted successfully';
|
|
39
|
-
break;
|
|
40
|
-
case 'GET':
|
|
41
|
-
message = 'Data retrieved successfully';
|
|
42
|
-
break;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
return {
|
|
46
|
-
success: true,
|
|
47
|
-
message,
|
|
48
|
-
data,
|
|
49
|
-
timestamp: new Date().toISOString(),
|
|
50
|
-
path: request.url,
|
|
51
|
-
};
|
|
52
|
-
}),
|
|
53
|
-
);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
1
|
+
import {
|
|
2
|
+
Injectable,
|
|
3
|
+
NestInterceptor,
|
|
4
|
+
ExecutionContext,
|
|
5
|
+
CallHandler,
|
|
6
|
+
HttpStatus,
|
|
7
|
+
} from '@nestjs/common';
|
|
8
|
+
import { Observable } from 'rxjs';
|
|
9
|
+
import { map } from 'rxjs/operators';
|
|
10
|
+
import { ApiResponseDto } from '@common/dto/api-response.dto';
|
|
11
|
+
|
|
12
|
+
@Injectable()
|
|
13
|
+
export class TransformInterceptor<T> implements NestInterceptor<T, ApiResponseDto<T>> {
|
|
14
|
+
intercept(context: ExecutionContext, next: CallHandler): Observable<ApiResponseDto<T>> {
|
|
15
|
+
const request = context.switchToHttp().getRequest();
|
|
16
|
+
const response = context.switchToHttp().getResponse();
|
|
17
|
+
|
|
18
|
+
return next.handle().pipe(
|
|
19
|
+
map((data) => {
|
|
20
|
+
// If data is already in ApiResponseDto format, return as is
|
|
21
|
+
if (data && typeof data === 'object' && 'success' in data && 'message' in data) {
|
|
22
|
+
return data;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Default success message based on HTTP method
|
|
26
|
+
let message = 'Operation successful';
|
|
27
|
+
|
|
28
|
+
switch (request.method) {
|
|
29
|
+
case 'POST':
|
|
30
|
+
message = 'Resource created successfully';
|
|
31
|
+
response.status(HttpStatus.CREATED);
|
|
32
|
+
break;
|
|
33
|
+
case 'PUT':
|
|
34
|
+
case 'PATCH':
|
|
35
|
+
message = 'Resource updated successfully';
|
|
36
|
+
break;
|
|
37
|
+
case 'DELETE':
|
|
38
|
+
message = 'Resource deleted successfully';
|
|
39
|
+
break;
|
|
40
|
+
case 'GET':
|
|
41
|
+
message = 'Data retrieved successfully';
|
|
42
|
+
break;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return {
|
|
46
|
+
success: true,
|
|
47
|
+
message,
|
|
48
|
+
data,
|
|
49
|
+
timestamp: new Date().toISOString(),
|
|
50
|
+
path: request.url,
|
|
51
|
+
};
|
|
52
|
+
}),
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { Global, Module } from '@nestjs/common';
|
|
2
|
-
import { PrismaService } from './prisma.service';
|
|
3
|
-
|
|
4
|
-
@Global()
|
|
5
|
-
@Module({
|
|
6
|
-
providers: [PrismaService],
|
|
7
|
-
exports: [PrismaService],
|
|
8
|
-
})
|
|
9
|
-
export class PrismaModule {}
|
|
1
|
+
import { Global, Module } from '@nestjs/common';
|
|
2
|
+
import { PrismaService } from './prisma.service';
|
|
3
|
+
|
|
4
|
+
@Global()
|
|
5
|
+
@Module({
|
|
6
|
+
providers: [PrismaService],
|
|
7
|
+
exports: [PrismaService],
|
|
8
|
+
})
|
|
9
|
+
export class PrismaModule {}
|