@traxionpay/cbsmiddleware 0.0.1
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/.prettierrc +4 -0
- package/Dockerfile +26 -0
- package/README.md +109 -0
- package/dist/app.module.d.ts +3 -0
- package/dist/app.module.d.ts.map +1 -0
- package/dist/app.module.js +44 -0
- package/dist/app.module.js.map +1 -0
- package/dist/auth/apikey.guard.d.ts +9 -0
- package/dist/auth/apikey.guard.d.ts.map +1 -0
- package/dist/auth/apikey.guard.js +86 -0
- package/dist/auth/apikey.guard.js.map +1 -0
- package/dist/auth/auth.controller.d.ts +10 -0
- package/dist/auth/auth.controller.d.ts.map +1 -0
- package/dist/auth/auth.controller.js +65 -0
- package/dist/auth/auth.controller.js.map +1 -0
- package/dist/auth/auth.interface.d.ts +8 -0
- package/dist/auth/auth.interface.d.ts.map +1 -0
- package/dist/auth/auth.interface.js +3 -0
- package/dist/auth/auth.interface.js.map +1 -0
- package/dist/auth/auth.module.d.ts +6 -0
- package/dist/auth/auth.module.d.ts.map +1 -0
- package/dist/auth/auth.module.js +81 -0
- package/dist/auth/auth.module.js.map +1 -0
- package/dist/auth/constant.d.ts +2 -0
- package/dist/auth/constant.d.ts.map +1 -0
- package/dist/auth/constant.js +5 -0
- package/dist/auth/constant.js.map +1 -0
- package/dist/auth/jwt-constant.d.ts +8 -0
- package/dist/auth/jwt-constant.d.ts.map +1 -0
- package/dist/auth/jwt-constant.js +60 -0
- package/dist/auth/jwt-constant.js.map +1 -0
- package/dist/auth/jwt-guard.d.ts +5 -0
- package/dist/auth/jwt-guard.d.ts.map +1 -0
- package/dist/auth/jwt-guard.js +18 -0
- package/dist/auth/jwt-guard.js.map +1 -0
- package/dist/auth/jwt-strategy.d.ts +16 -0
- package/dist/auth/jwt-strategy.d.ts.map +1 -0
- package/dist/auth/jwt-strategy.js +59 -0
- package/dist/auth/jwt-strategy.js.map +1 -0
- package/dist/auth/services/auth.service.d.ts +14 -0
- package/dist/auth/services/auth.service.d.ts.map +1 -0
- package/dist/auth/services/auth.service.js +56 -0
- package/dist/auth/services/auth.service.js.map +1 -0
- package/dist/auth/services/encryption.service.d.ts +11 -0
- package/dist/auth/services/encryption.service.d.ts.map +1 -0
- package/dist/auth/services/encryption.service.js +49 -0
- package/dist/auth/services/encryption.service.js.map +1 -0
- package/dist/auth.config.d.ts +29 -0
- package/dist/auth.config.d.ts.map +1 -0
- package/dist/auth.config.js +71 -0
- package/dist/auth.config.js.map +1 -0
- package/dist/banking/banking.controller.d.ts +18 -0
- package/dist/banking/banking.controller.d.ts.map +1 -0
- package/dist/banking/banking.controller.js +146 -0
- package/dist/banking/banking.controller.js.map +1 -0
- package/dist/banking/banking.module.d.ts +6 -0
- package/dist/banking/banking.module.d.ts.map +1 -0
- package/dist/banking/banking.module.js +90 -0
- package/dist/banking/banking.module.js.map +1 -0
- package/dist/banking/dto/account-transfer.dto.d.ts +17 -0
- package/dist/banking/dto/account-transfer.dto.d.ts.map +1 -0
- package/dist/banking/dto/account-transfer.dto.js +101 -0
- package/dist/banking/dto/account-transfer.dto.js.map +1 -0
- package/dist/banking/dto/account.dto.d.ts +19 -0
- package/dist/banking/dto/account.dto.d.ts.map +1 -0
- package/dist/banking/dto/account.dto.js +104 -0
- package/dist/banking/dto/account.dto.js.map +1 -0
- package/dist/banking/dto/fetch-bank-balance.dto.d.ts +12 -0
- package/dist/banking/dto/fetch-bank-balance.dto.d.ts.map +1 -0
- package/dist/banking/dto/fetch-bank-balance.dto.js +71 -0
- package/dist/banking/dto/fetch-bank-balance.dto.js.map +1 -0
- package/dist/banking/dto/transaction-history.dto.d.ts +15 -0
- package/dist/banking/dto/transaction-history.dto.d.ts.map +1 -0
- package/dist/banking/dto/transaction-history.dto.js +89 -0
- package/dist/banking/dto/transaction-history.dto.js.map +1 -0
- package/dist/banking/factory/bank-strategy.factory.d.ts +9 -0
- package/dist/banking/factory/bank-strategy.factory.d.ts.map +1 -0
- package/dist/banking/factory/bank-strategy.factory.js +48 -0
- package/dist/banking/factory/bank-strategy.factory.js.map +1 -0
- package/dist/banking/interfaces/bank-config.interface.d.ts +22 -0
- package/dist/banking/interfaces/bank-config.interface.d.ts.map +1 -0
- package/dist/banking/interfaces/bank-config.interface.js +3 -0
- package/dist/banking/interfaces/bank-config.interface.js.map +1 -0
- package/dist/banking/interfaces/bank-service.interface.d.ts +19 -0
- package/dist/banking/interfaces/bank-service.interface.d.ts.map +1 -0
- package/dist/banking/interfaces/bank-service.interface.js +3 -0
- package/dist/banking/interfaces/bank-service.interface.js.map +1 -0
- package/dist/banking/services/ASPAC.service.d.ts +25 -0
- package/dist/banking/services/ASPAC.service.d.ts.map +1 -0
- package/dist/banking/services/ASPAC.service.js +498 -0
- package/dist/banking/services/ASPAC.service.js.map +1 -0
- package/dist/banking/services/BankGateway.service.d.ts +18 -0
- package/dist/banking/services/BankGateway.service.d.ts.map +1 -0
- package/dist/banking/services/BankGateway.service.js +71 -0
- package/dist/banking/services/BankGateway.service.js.map +1 -0
- package/dist/banking/services/MBWIN.service.d.ts +25 -0
- package/dist/banking/services/MBWIN.service.d.ts.map +1 -0
- package/dist/banking/services/MBWIN.service.js +655 -0
- package/dist/banking/services/MBWIN.service.js.map +1 -0
- package/dist/banking/services/WelcomeBank.service.d.ts +29 -0
- package/dist/banking/services/WelcomeBank.service.d.ts.map +1 -0
- package/dist/banking/services/WelcomeBank.service.js +840 -0
- package/dist/banking/services/WelcomeBank.service.js.map +1 -0
- package/dist/banking/services/sms.service.d.ts +10 -0
- package/dist/banking/services/sms.service.d.ts.map +1 -0
- package/dist/banking/services/sms.service.js +97 -0
- package/dist/banking/services/sms.service.js.map +1 -0
- package/dist/banking/tokens/bank-service.tokens.d.ts +5 -0
- package/dist/banking/tokens/bank-service.tokens.d.ts.map +1 -0
- package/dist/banking/tokens/bank-service.tokens.js +8 -0
- package/dist/banking/tokens/bank-service.tokens.js.map +1 -0
- package/dist/banking/types.d.ts +10 -0
- package/dist/banking/types.d.ts.map +1 -0
- package/dist/banking/types.js +3 -0
- package/dist/banking/types.js.map +1 -0
- package/dist/decorators/response_message.decorator.d.ts +2 -0
- package/dist/decorators/response_message.decorator.d.ts.map +1 -0
- package/dist/decorators/response_message.decorator.js +7 -0
- package/dist/decorators/response_message.decorator.js.map +1 -0
- package/dist/helpers/axios-error.helper.d.ts +6 -0
- package/dist/helpers/axios-error.helper.d.ts.map +1 -0
- package/dist/helpers/axios-error.helper.js +78 -0
- package/dist/helpers/axios-error.helper.js.map +1 -0
- package/dist/helpers/response-format.helper.d.ts +4 -0
- package/dist/helpers/response-format.helper.d.ts.map +1 -0
- package/dist/helpers/response-format.helper.js +15 -0
- package/dist/helpers/response-format.helper.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +26 -0
- package/dist/index.js.map +1 -0
- package/dist/interceptors/response.interceptor.d.ts +16 -0
- package/dist/interceptors/response.interceptor.d.ts.map +1 -0
- package/dist/interceptors/response.interceptor.js +80 -0
- package/dist/interceptors/response.interceptor.js.map +1 -0
- package/dist/logger/logger-interceptor.d.ts +14 -0
- package/dist/logger/logger-interceptor.d.ts.map +1 -0
- package/dist/logger/logger-interceptor.js +60 -0
- package/dist/logger/logger-interceptor.js.map +1 -0
- package/dist/logger/logger-option.d.ts +6 -0
- package/dist/logger/logger-option.d.ts.map +1 -0
- package/dist/logger/logger-option.js +103 -0
- package/dist/logger/logger-option.js.map +1 -0
- package/dist/logger/logger.config.d.ts +3 -0
- package/dist/logger/logger.config.d.ts.map +1 -0
- package/dist/logger/logger.config.js +31 -0
- package/dist/logger/logger.config.js.map +1 -0
- package/dist/logger/logger.module.d.ts +3 -0
- package/dist/logger/logger.module.d.ts.map +1 -0
- package/dist/logger/logger.module.js +76 -0
- package/dist/logger/logger.module.js.map +1 -0
- package/dist/main.d.ts +2 -0
- package/dist/main.d.ts.map +1 -0
- package/dist/main.js +13 -0
- package/dist/main.js.map +1 -0
- package/dist/tsconfig.build.tsbuildinfo +1 -0
- package/docker-compose.yml +14 -0
- package/ecosystem.config.js +35 -0
- package/eslint.config.mjs +34 -0
- package/nest-cli.json +8 -0
- package/package.json +102 -0
- package/src/app.module.ts +33 -0
- package/src/auth/apikey.guard.ts +42 -0
- package/src/auth/auth.controller.ts +41 -0
- package/src/auth/auth.interface.ts +7 -0
- package/src/auth/auth.module.ts +125 -0
- package/src/auth/constant.ts +1 -0
- package/src/auth/jwt-constant.ts +31 -0
- package/src/auth/jwt-guard.ts +5 -0
- package/src/auth/jwt-strategy.ts +43 -0
- package/src/auth/services/auth.service.ts +33 -0
- package/src/auth/services/encryption.service.ts +35 -0
- package/src/auth.config.ts +42 -0
- package/src/banking/banking.controller.ts +142 -0
- package/src/banking/banking.module.ts +142 -0
- package/src/banking/dto/account-transfer.dto.ts +66 -0
- package/src/banking/dto/account.dto.ts +70 -0
- package/src/banking/dto/fetch-bank-balance.dto.ts +46 -0
- package/src/banking/dto/transaction-history.dto.ts +58 -0
- package/src/banking/factory/bank-strategy.factory.ts +25 -0
- package/src/banking/interfaces/bank-config.interface.ts +25 -0
- package/src/banking/interfaces/bank-service.interface.ts +164 -0
- package/src/banking/services/ASPAC.service.ts +574 -0
- package/src/banking/services/BankGateway.service.ts +64 -0
- package/src/banking/services/MBWIN.service.ts +779 -0
- package/src/banking/services/WelcomeBank.service.ts +990 -0
- package/src/banking/services/sms.service.ts +50 -0
- package/src/banking/tokens/bank-service.tokens.ts +4 -0
- package/src/banking/types.ts +10 -0
- package/src/decorators/response_message.decorator.ts +4 -0
- package/src/helpers/axios-error.helper.ts +63 -0
- package/src/helpers/response-format.helper.ts +15 -0
- package/src/index.ts +15 -0
- package/src/interceptors/response.interceptor.ts +53 -0
- package/src/logger/logger-interceptor.ts +55 -0
- package/src/logger/logger-option.ts +119 -0
- package/src/logger/logger.config.ts +31 -0
- package/src/logger/logger.module.ts +31 -0
- package/src/main.ts +10 -0
- package/src/types/pino-daily-rotate-file.d.ts +31 -0
- package/tsconfig.build.json +11 -0
- package/tsconfig.build.tsbuildinfo +1 -0
- package/tsconfig.json +22 -0
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
|
|
2
|
+
import { Injectable } from '@nestjs/common';
|
|
3
|
+
import { Logger } from 'nestjs-pino';
|
|
4
|
+
import axios from 'axios';
|
|
5
|
+
import * as process from 'node:process';
|
|
6
|
+
import { ConfigService } from '@nestjs/config';
|
|
7
|
+
|
|
8
|
+
@Injectable()
|
|
9
|
+
export class SmsService {
|
|
10
|
+
constructor(
|
|
11
|
+
private readonly logger: Logger,
|
|
12
|
+
private readonly configService: ConfigService
|
|
13
|
+
) {}
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
async sendSms(mobile: string, otp: string, bankName: string) {
|
|
17
|
+
const url = process.env.SMS_URL || 'https://opensms.traxiontech.net/sms/push/';
|
|
18
|
+
const message = this.generateSmsMessage(otp, bankName);
|
|
19
|
+
|
|
20
|
+
const payload = {
|
|
21
|
+
number: mobile,
|
|
22
|
+
message,
|
|
23
|
+
};
|
|
24
|
+
const headers = {
|
|
25
|
+
'Content-Type': 'application/json',
|
|
26
|
+
'X-TXN-Auth': process.env.SMS_API_KEY || 'myt6tjeZ.wNOZPHckjolKNG4oVn3LGVVAHcZ1NHty',
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
// log without await
|
|
30
|
+
axios
|
|
31
|
+
.post(url, payload, { headers })
|
|
32
|
+
.then((response) => {
|
|
33
|
+
this.logger.log({ "Send SMS:": response.data });
|
|
34
|
+
})
|
|
35
|
+
.catch((error: any) => {
|
|
36
|
+
this.logger.error({
|
|
37
|
+
"Error sending SMS:": error.response?.data || error.message,
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
private generateSmsMessage(otp: string, BankName: string): string {
|
|
43
|
+
let template = this.configService.get<string>('SMS_OTP_MESSAGE') ||
|
|
44
|
+
'Your {{BankName}} One-Time PIN is {{otp}}. DO NOT SHARE THIS WITH ANYONE.';
|
|
45
|
+
template = template.replace('{{otp}}', otp);
|
|
46
|
+
template = template.replace('{{BankName}}', BankName);
|
|
47
|
+
return template;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Type } from '@nestjs/common';
|
|
2
|
+
import { BankService } from './interfaces/bank-service.interface';
|
|
3
|
+
import { BankingConfig } from './interfaces/bank-config.interface';
|
|
4
|
+
|
|
5
|
+
export interface BankingModuleOptions {
|
|
6
|
+
ASPACService?: Type<BankService>;
|
|
7
|
+
WelcomeBankService?: Type<BankService>;
|
|
8
|
+
MBWINService?: Type<BankService>;
|
|
9
|
+
config?: BankingConfig;
|
|
10
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { HttpException, HttpStatus, Logger } from '@nestjs/common';
|
|
2
|
+
import { AxiosError } from 'axios';
|
|
3
|
+
import * as moment from 'moment-timezone';
|
|
4
|
+
|
|
5
|
+
export class AxiosErrorHelper {
|
|
6
|
+
private static readonly logger = new Logger(AxiosErrorHelper.name);
|
|
7
|
+
|
|
8
|
+
static handleAxiosError(error: unknown ): never {
|
|
9
|
+
const timestamp = moment.tz('Asia/Manila').format('YYYY-MM-DD HH:mm:ss');
|
|
10
|
+
// Check if the error is an AxiosError
|
|
11
|
+
if (this.isAxiosError(error)) {
|
|
12
|
+
if (error.response) {
|
|
13
|
+
// Log the error from the external service
|
|
14
|
+
this.logger.error(
|
|
15
|
+
`External service error: ${error.response.status} - ${error.response.statusText}`,
|
|
16
|
+
JSON.stringify(error.response.data),
|
|
17
|
+
);
|
|
18
|
+
throw new HttpException(
|
|
19
|
+
{
|
|
20
|
+
statusCode: error.response.status || HttpStatus.INTERNAL_SERVER_ERROR,
|
|
21
|
+
timestamp,
|
|
22
|
+
message: 'Internal error',
|
|
23
|
+
data: error.response.data || 'Service error',
|
|
24
|
+
},
|
|
25
|
+
error.response.status || HttpStatus.INTERNAL_SERVER_ERROR,
|
|
26
|
+
);
|
|
27
|
+
} else if (error.request) {
|
|
28
|
+
// Log the case where no response is received
|
|
29
|
+
this.logger.error(
|
|
30
|
+
'No response received from external service. The service may be down.',
|
|
31
|
+
);
|
|
32
|
+
throw new HttpException(
|
|
33
|
+
{
|
|
34
|
+
statusCode: HttpStatus.SERVICE_UNAVAILABLE,
|
|
35
|
+
timestamp,
|
|
36
|
+
message: 'Service is unreachable. Please try again later.',
|
|
37
|
+
},
|
|
38
|
+
HttpStatus.SERVICE_UNAVAILABLE,
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Log and handle non-Axios errors
|
|
44
|
+
const message =
|
|
45
|
+
error instanceof Error
|
|
46
|
+
? error.message
|
|
47
|
+
: 'An unexpected error occurred';
|
|
48
|
+
this.logger.error(`Unexpected error: ${message}`, JSON.stringify(error));
|
|
49
|
+
throw new HttpException(
|
|
50
|
+
{
|
|
51
|
+
statusCode: HttpStatus.INTERNAL_SERVER_ERROR,
|
|
52
|
+
timestamp,
|
|
53
|
+
message: 'An unexpected error occurred',
|
|
54
|
+
error: message,
|
|
55
|
+
},
|
|
56
|
+
HttpStatus.INTERNAL_SERVER_ERROR,
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
private static isAxiosError(error: unknown): error is AxiosError {
|
|
61
|
+
return (error as AxiosError).isAxiosError !== undefined;
|
|
62
|
+
}
|
|
63
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/* export * from './banking/interfaces/bank-service.interface';
|
|
2
|
+
export * from './banking/tokens/bank-service.tokens';
|
|
3
|
+
export * from './banking/types'; */
|
|
4
|
+
export * from './banking/banking.module';
|
|
5
|
+
export * from './auth/auth.module';
|
|
6
|
+
export * from './banking/banking.controller';
|
|
7
|
+
export * from './banking/factory/bank-strategy.factory';
|
|
8
|
+
export * from './banking/dto/fetch-bank-balance.dto';
|
|
9
|
+
export * from './logger/logger.module'
|
|
10
|
+
export * from './logger/logger-interceptor'
|
|
11
|
+
export * from './interceptors/response.interceptor'
|
|
12
|
+
export * from './banking/services/BankGateway.service'
|
|
13
|
+
/* export * from './auth/services/auth.service';
|
|
14
|
+
export * from './auth/jwt-guard';
|
|
15
|
+
export * from './auth/auth.controller'; */
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Injectable,
|
|
3
|
+
NestInterceptor,
|
|
4
|
+
ExecutionContext,
|
|
5
|
+
CallHandler,
|
|
6
|
+
} from '@nestjs/common';
|
|
7
|
+
import { Reflector } from '@nestjs/core';
|
|
8
|
+
import { Observable } from 'rxjs';
|
|
9
|
+
import { map } from 'rxjs/operators';
|
|
10
|
+
import * as moment from 'moment-timezone';
|
|
11
|
+
export interface Response<T> {
|
|
12
|
+
statusCode: number;
|
|
13
|
+
timestamp: string;
|
|
14
|
+
message: string;
|
|
15
|
+
data: T | null;
|
|
16
|
+
customCode?: number; // Custom error
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
@Injectable()
|
|
20
|
+
export class TransformInterceptor<T> implements NestInterceptor<T, Response<T>> {
|
|
21
|
+
constructor(private reflector: Reflector) {}
|
|
22
|
+
|
|
23
|
+
intercept(
|
|
24
|
+
context: ExecutionContext,
|
|
25
|
+
next: CallHandler,
|
|
26
|
+
): Observable<Response<T>> {
|
|
27
|
+
return next.handle().pipe(
|
|
28
|
+
map((data) => {
|
|
29
|
+
// Check if the data has a status of 400 or 500
|
|
30
|
+
if (data.status && (data.status !== 200 || data.status !== 201)) {
|
|
31
|
+
// Modify response for error statuses (400 or 500)
|
|
32
|
+
return {
|
|
33
|
+
statusCode: context.switchToHttp().getResponse().statusCode,
|
|
34
|
+
timestamp: moment.tz('Asia/Manila').format('YYYY-MM-DD HH:mm:ss'),
|
|
35
|
+
message: `An error occurred: ${data.status}`, // Custom error message
|
|
36
|
+
data: null, // Set data to null for errors
|
|
37
|
+
// customCode: data.status === 400 ? 4000 : 5000, // Custom error code (adjust as necessary)
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Default response for normal successful responses
|
|
42
|
+
return {
|
|
43
|
+
statusCode: context.switchToHttp().getResponse().statusCode,
|
|
44
|
+
timestamp: moment.tz('Asia/Manila').format('YYYY-MM-DD HH:mm:ss'),
|
|
45
|
+
message:
|
|
46
|
+
this.reflector.get<string>('response_message', context.getHandler()) ||
|
|
47
|
+
'',
|
|
48
|
+
data,
|
|
49
|
+
};
|
|
50
|
+
}),
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Injectable,
|
|
3
|
+
NestInterceptor,
|
|
4
|
+
ExecutionContext,
|
|
5
|
+
CallHandler,
|
|
6
|
+
} from '@nestjs/common';
|
|
7
|
+
import { Logger } from 'nestjs-pino';
|
|
8
|
+
import { catchError, Observable, tap, throwError } from 'rxjs';
|
|
9
|
+
|
|
10
|
+
@Injectable()
|
|
11
|
+
export class LoggerInterceptor implements NestInterceptor {
|
|
12
|
+
constructor(
|
|
13
|
+
private readonly logger: Logger
|
|
14
|
+
) {}
|
|
15
|
+
|
|
16
|
+
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
|
|
17
|
+
const req = context.switchToHttp().getRequest();
|
|
18
|
+
const method = req.method;
|
|
19
|
+
const url = req.url;
|
|
20
|
+
const startTime = Date.now();
|
|
21
|
+
const body = req.body;
|
|
22
|
+
const safeBody = { ...body };
|
|
23
|
+
delete safeBody.password;
|
|
24
|
+
// delete safeBody.otp;
|
|
25
|
+
|
|
26
|
+
this.logger.log({ method, url, body: safeBody }, 'Request started');
|
|
27
|
+
|
|
28
|
+
return next.handle().pipe(
|
|
29
|
+
tap(() => {
|
|
30
|
+
const res = context.switchToHttp().getResponse();
|
|
31
|
+
const statusCode = res.statusCode;
|
|
32
|
+
const processTime = Date.now() - startTime;
|
|
33
|
+
|
|
34
|
+
this.logger.log(
|
|
35
|
+
{ statusCode, processTime, body: safeBody },
|
|
36
|
+
'Request processed',
|
|
37
|
+
);
|
|
38
|
+
}),
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
@Injectable()
|
|
44
|
+
export class ErrorInterceptor implements NestInterceptor {
|
|
45
|
+
constructor(private readonly logger: Logger) {}
|
|
46
|
+
|
|
47
|
+
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
|
|
48
|
+
return next.handle().pipe(
|
|
49
|
+
catchError((error) => {
|
|
50
|
+
this.logger.error({ error: error.stack }, 'Intercept Error occurred');
|
|
51
|
+
return throwError(() => error);
|
|
52
|
+
}),
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import pino from 'pino';
|
|
2
|
+
import * as path from 'node:path';
|
|
3
|
+
import * as fs from 'node:fs';
|
|
4
|
+
|
|
5
|
+
// export class LoggerFilename {
|
|
6
|
+
// Ensure log directory and file exist
|
|
7
|
+
export const ensureLogFileExists = (filePath: string) => {
|
|
8
|
+
const logDir = path.dirname(filePath);
|
|
9
|
+
|
|
10
|
+
if (!fs.existsSync(logDir)) {
|
|
11
|
+
fs.mkdirSync(logDir, { recursive: true });
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
if (!fs.existsSync(filePath)) {
|
|
15
|
+
fs.writeFileSync(filePath, '', { flag: 'w' }); // Create a writable stream for logging
|
|
16
|
+
}
|
|
17
|
+
return fs.createWriteStream(filePath, { flags: 'a' }); // Append mode
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
// Helper function to generate the filename based on the current date
|
|
21
|
+
export const generateLogFilename = () => {
|
|
22
|
+
const date = new Date();
|
|
23
|
+
const year = date.getFullYear();
|
|
24
|
+
const month = String(date.getMonth() + 1).padStart(2, '0'); // Months are zero-indexed
|
|
25
|
+
const day = String(date.getDate()).padStart(2, '0');
|
|
26
|
+
return `./logs/bank-middleware-${year}-${month}-${day}.log`; // Example: ./logs/2024-11-23.log
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
// Create a custom transport with fallback
|
|
30
|
+
export const createFaultTolerantTransport = (): pino.TransportMultiOptions => {
|
|
31
|
+
const defaultOpts = {
|
|
32
|
+
singleLine: true,
|
|
33
|
+
mkdir: true,
|
|
34
|
+
prettyPrint: true,
|
|
35
|
+
translateTime: "yyyy-MM-dd'T'HH:mm:ss.l'Z'",
|
|
36
|
+
// messageFormat:
|
|
37
|
+
// '{req.headers.x-correlation-id} [{context}] {responseTime} {msg}',
|
|
38
|
+
// ignore: 'pid,hostname,context,req,res,responseTime',
|
|
39
|
+
errorLikeObjectKeys: ['err', 'error'],
|
|
40
|
+
frequency: 'daily',
|
|
41
|
+
zippedArchive: false,
|
|
42
|
+
maxFiles: '30d', // Keep logs for 30 days
|
|
43
|
+
colorize: true,
|
|
44
|
+
datePattern: 'YYYY-MM-DD',
|
|
45
|
+
};
|
|
46
|
+
const logFilename = generateLogFilename();
|
|
47
|
+
const logStream = ensureLogFileExists(logFilename);
|
|
48
|
+
|
|
49
|
+
// Periodically check for file existence and handle interruptions
|
|
50
|
+
setInterval(() => {
|
|
51
|
+
if (!fs.existsSync(logFilename)) {
|
|
52
|
+
console.error(`Log file missing: ${logFilename}`);
|
|
53
|
+
logStream.end();
|
|
54
|
+
ensureLogFileExists(logFilename);
|
|
55
|
+
console.error(`Log file recreated: ${logFilename}`);
|
|
56
|
+
}
|
|
57
|
+
}, 5000);
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
targets: [
|
|
61
|
+
{
|
|
62
|
+
target: 'pino-pretty', // Console logs for development
|
|
63
|
+
level: 'debug',
|
|
64
|
+
options: {
|
|
65
|
+
...defaultOpts,
|
|
66
|
+
errorLikeObjectKeys: ['err', 'error'],
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
target: 'pino/file', // File transport
|
|
71
|
+
level: 'debug',
|
|
72
|
+
options: {
|
|
73
|
+
destination: logFilename,
|
|
74
|
+
...defaultOpts, // Ensure directories are created automatically
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
// {
|
|
78
|
+
// level: 'debug',
|
|
79
|
+
// target: 'pino-daily-rotate-file',
|
|
80
|
+
// options: {
|
|
81
|
+
// destination: logFilename,
|
|
82
|
+
// ...defaultOpts,
|
|
83
|
+
// maxSize: '10m',
|
|
84
|
+
// gzip: true,
|
|
85
|
+
// },
|
|
86
|
+
// },
|
|
87
|
+
/* {
|
|
88
|
+
level: 'debug',
|
|
89
|
+
target: 'pino-postgresql-transport',
|
|
90
|
+
options: {
|
|
91
|
+
serviceName: 'TRANSACTION_SERVICE',
|
|
92
|
+
tableName: 'ApplicationLogs',
|
|
93
|
+
connection: {
|
|
94
|
+
host: process.env.LOG_DATABASE_HOST,
|
|
95
|
+
port: process.env.LOG_DATABASE_PORT,
|
|
96
|
+
user: process.env.LOG_DATABASE_USER,
|
|
97
|
+
password: process.env.LOG_DATABASE_PASSWORD,
|
|
98
|
+
database: process.env.LOG_DATABASE_NAME,
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
level: 'error',
|
|
104
|
+
target: 'google-chat-transport',
|
|
105
|
+
options: {
|
|
106
|
+
webhookUrl: process.env.GOOGLE_CHAT_SPACES_WEBHOOK_URL,
|
|
107
|
+
verbose: true,
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
target: 'pino-elastic', // Optional: external log aggregation service
|
|
112
|
+
level: 'error',
|
|
113
|
+
options: {
|
|
114
|
+
url: 'http://your-log-server:9200', // Example for Elasticsearch
|
|
115
|
+
},
|
|
116
|
+
},*/
|
|
117
|
+
],
|
|
118
|
+
};
|
|
119
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { LoggerOptions } from 'pino';
|
|
2
|
+
import PinoDailyRotateFile from 'pino-daily-rotate-file';
|
|
3
|
+
import { format } from 'date-fns-tz'; // Import date-fns-tz for timezone handling
|
|
4
|
+
|
|
5
|
+
export function createLoggerOptions(): LoggerOptions {
|
|
6
|
+
// Function to generate a UTC-based log filename with date
|
|
7
|
+
const generateLogFilename = (): string => {
|
|
8
|
+
const now = new Date();
|
|
9
|
+
const utcDate = format(now, 'yyyy-MM-dd', { timeZone: 'UTC' }); // Format date in UTC
|
|
10
|
+
return `./logs/application-${utcDate}.log`; // Return log file with UTC date
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
// Create the transport using PinoDailyRotateFile constructor
|
|
14
|
+
const transport = new PinoDailyRotateFile({
|
|
15
|
+
filename: generateLogFilename(), // Dynamically use UTC date
|
|
16
|
+
datePattern: 'yyyy-MM-dd', // Daily rotation pattern
|
|
17
|
+
maxSize: '20m', // Max size of a log file (20MB)
|
|
18
|
+
maxFiles: '14d', // Retain logs for 14 days
|
|
19
|
+
gzip: true, // Compress old logs
|
|
20
|
+
utc: true, // Ensure UTC-based rotation
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
// Return the logger options
|
|
24
|
+
return {
|
|
25
|
+
level: 'info',
|
|
26
|
+
transport: {
|
|
27
|
+
target: 'pino/file', // Specify transport target
|
|
28
|
+
options: { stream: transport }, // Pass the created stream
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Module } from '@nestjs/common';
|
|
2
|
+
import { ConfigModule, ConfigService } from '@nestjs/config';
|
|
3
|
+
import { LoggerModule } from 'nestjs-pino';
|
|
4
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
5
|
+
import { createFaultTolerantTransport } from './logger-option';
|
|
6
|
+
import * as moment from 'moment-timezone';
|
|
7
|
+
|
|
8
|
+
@Module({
|
|
9
|
+
imports: [
|
|
10
|
+
ConfigModule,
|
|
11
|
+
LoggerModule.forRootAsync({
|
|
12
|
+
imports: [ConfigModule],
|
|
13
|
+
inject: [ConfigService],
|
|
14
|
+
useFactory: async () => {
|
|
15
|
+
return {
|
|
16
|
+
pinoHttp: {
|
|
17
|
+
level: 'debug',
|
|
18
|
+
// timestamp: pino.stdTimeFunctions.isoTime,
|
|
19
|
+
timestamp: () => `,"time":"${moment.tz('Asia/Manila').format('YYYY-MM-DD HH:mm:ss')}"`,
|
|
20
|
+
genReqId: (request) => request.id || uuidv4(),
|
|
21
|
+
redact: ['request.headers.authorization'],
|
|
22
|
+
quietReqLogger: true,
|
|
23
|
+
autoLogging: true,
|
|
24
|
+
transport: createFaultTolerantTransport(),
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
},
|
|
28
|
+
}),
|
|
29
|
+
],
|
|
30
|
+
})
|
|
31
|
+
export class AppLoggerModule {}
|
package/src/main.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { NestFactory } from '@nestjs/core';
|
|
2
|
+
import { AppModule } from './app.module';
|
|
3
|
+
import { Logger } from 'nestjs-pino';
|
|
4
|
+
async function bootstrap() {
|
|
5
|
+
const app = await NestFactory.create(AppModule);
|
|
6
|
+
const logger = app.get(Logger);
|
|
7
|
+
app.useLogger(logger);
|
|
8
|
+
await app.listen(process.env.PORT ?? 5002);
|
|
9
|
+
}
|
|
10
|
+
bootstrap();
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
declare module 'pino-daily-rotate-file' {
|
|
2
|
+
import { Writable } from 'stream';
|
|
3
|
+
|
|
4
|
+
// interface PinoDailyRotateOptions {
|
|
5
|
+
// filename: string; // Path pattern for log files (e.g., './logs/log-%DATE%.log')
|
|
6
|
+
// datePattern?: string; // Date format in filenames (e.g., 'YYYY-MM-DD')
|
|
7
|
+
// maxSize?: string; // Max size of a log file (e.g., '10m')
|
|
8
|
+
// maxFiles?: string | number; // Max number of files to keep (e.g., '14d')
|
|
9
|
+
// utc?: boolean; // Use UTC time in log file names
|
|
10
|
+
// auditFile?: string; // Path to audit JSON file
|
|
11
|
+
// gzip?: boolean; // Compress old log files
|
|
12
|
+
// extension?: string; // Extension for log files (e.g., '.log')
|
|
13
|
+
// level?: string;
|
|
14
|
+
// }
|
|
15
|
+
|
|
16
|
+
class PinoDailyRotateFile extends Writable {
|
|
17
|
+
constructor(options: {
|
|
18
|
+
filename: string;
|
|
19
|
+
datePattern: string;
|
|
20
|
+
maxSize: string;
|
|
21
|
+
maxFiles: string;
|
|
22
|
+
level?: string;
|
|
23
|
+
gzip?: boolean; // Compress old log files
|
|
24
|
+
utc?: boolean; // Use UTC time in log file names
|
|
25
|
+
auditFile?: string; // Path to audit JSON file
|
|
26
|
+
extension?: string; // Extension for log files (e.g., '.log')
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export = PinoDailyRotateFile;
|
|
31
|
+
}
|