@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.
Files changed (203) hide show
  1. package/.prettierrc +4 -0
  2. package/Dockerfile +26 -0
  3. package/README.md +109 -0
  4. package/dist/app.module.d.ts +3 -0
  5. package/dist/app.module.d.ts.map +1 -0
  6. package/dist/app.module.js +44 -0
  7. package/dist/app.module.js.map +1 -0
  8. package/dist/auth/apikey.guard.d.ts +9 -0
  9. package/dist/auth/apikey.guard.d.ts.map +1 -0
  10. package/dist/auth/apikey.guard.js +86 -0
  11. package/dist/auth/apikey.guard.js.map +1 -0
  12. package/dist/auth/auth.controller.d.ts +10 -0
  13. package/dist/auth/auth.controller.d.ts.map +1 -0
  14. package/dist/auth/auth.controller.js +65 -0
  15. package/dist/auth/auth.controller.js.map +1 -0
  16. package/dist/auth/auth.interface.d.ts +8 -0
  17. package/dist/auth/auth.interface.d.ts.map +1 -0
  18. package/dist/auth/auth.interface.js +3 -0
  19. package/dist/auth/auth.interface.js.map +1 -0
  20. package/dist/auth/auth.module.d.ts +6 -0
  21. package/dist/auth/auth.module.d.ts.map +1 -0
  22. package/dist/auth/auth.module.js +81 -0
  23. package/dist/auth/auth.module.js.map +1 -0
  24. package/dist/auth/constant.d.ts +2 -0
  25. package/dist/auth/constant.d.ts.map +1 -0
  26. package/dist/auth/constant.js +5 -0
  27. package/dist/auth/constant.js.map +1 -0
  28. package/dist/auth/jwt-constant.d.ts +8 -0
  29. package/dist/auth/jwt-constant.d.ts.map +1 -0
  30. package/dist/auth/jwt-constant.js +60 -0
  31. package/dist/auth/jwt-constant.js.map +1 -0
  32. package/dist/auth/jwt-guard.d.ts +5 -0
  33. package/dist/auth/jwt-guard.d.ts.map +1 -0
  34. package/dist/auth/jwt-guard.js +18 -0
  35. package/dist/auth/jwt-guard.js.map +1 -0
  36. package/dist/auth/jwt-strategy.d.ts +16 -0
  37. package/dist/auth/jwt-strategy.d.ts.map +1 -0
  38. package/dist/auth/jwt-strategy.js +59 -0
  39. package/dist/auth/jwt-strategy.js.map +1 -0
  40. package/dist/auth/services/auth.service.d.ts +14 -0
  41. package/dist/auth/services/auth.service.d.ts.map +1 -0
  42. package/dist/auth/services/auth.service.js +56 -0
  43. package/dist/auth/services/auth.service.js.map +1 -0
  44. package/dist/auth/services/encryption.service.d.ts +11 -0
  45. package/dist/auth/services/encryption.service.d.ts.map +1 -0
  46. package/dist/auth/services/encryption.service.js +49 -0
  47. package/dist/auth/services/encryption.service.js.map +1 -0
  48. package/dist/auth.config.d.ts +29 -0
  49. package/dist/auth.config.d.ts.map +1 -0
  50. package/dist/auth.config.js +71 -0
  51. package/dist/auth.config.js.map +1 -0
  52. package/dist/banking/banking.controller.d.ts +18 -0
  53. package/dist/banking/banking.controller.d.ts.map +1 -0
  54. package/dist/banking/banking.controller.js +146 -0
  55. package/dist/banking/banking.controller.js.map +1 -0
  56. package/dist/banking/banking.module.d.ts +6 -0
  57. package/dist/banking/banking.module.d.ts.map +1 -0
  58. package/dist/banking/banking.module.js +90 -0
  59. package/dist/banking/banking.module.js.map +1 -0
  60. package/dist/banking/dto/account-transfer.dto.d.ts +17 -0
  61. package/dist/banking/dto/account-transfer.dto.d.ts.map +1 -0
  62. package/dist/banking/dto/account-transfer.dto.js +101 -0
  63. package/dist/banking/dto/account-transfer.dto.js.map +1 -0
  64. package/dist/banking/dto/account.dto.d.ts +19 -0
  65. package/dist/banking/dto/account.dto.d.ts.map +1 -0
  66. package/dist/banking/dto/account.dto.js +104 -0
  67. package/dist/banking/dto/account.dto.js.map +1 -0
  68. package/dist/banking/dto/fetch-bank-balance.dto.d.ts +12 -0
  69. package/dist/banking/dto/fetch-bank-balance.dto.d.ts.map +1 -0
  70. package/dist/banking/dto/fetch-bank-balance.dto.js +71 -0
  71. package/dist/banking/dto/fetch-bank-balance.dto.js.map +1 -0
  72. package/dist/banking/dto/transaction-history.dto.d.ts +15 -0
  73. package/dist/banking/dto/transaction-history.dto.d.ts.map +1 -0
  74. package/dist/banking/dto/transaction-history.dto.js +89 -0
  75. package/dist/banking/dto/transaction-history.dto.js.map +1 -0
  76. package/dist/banking/factory/bank-strategy.factory.d.ts +9 -0
  77. package/dist/banking/factory/bank-strategy.factory.d.ts.map +1 -0
  78. package/dist/banking/factory/bank-strategy.factory.js +48 -0
  79. package/dist/banking/factory/bank-strategy.factory.js.map +1 -0
  80. package/dist/banking/interfaces/bank-config.interface.d.ts +22 -0
  81. package/dist/banking/interfaces/bank-config.interface.d.ts.map +1 -0
  82. package/dist/banking/interfaces/bank-config.interface.js +3 -0
  83. package/dist/banking/interfaces/bank-config.interface.js.map +1 -0
  84. package/dist/banking/interfaces/bank-service.interface.d.ts +19 -0
  85. package/dist/banking/interfaces/bank-service.interface.d.ts.map +1 -0
  86. package/dist/banking/interfaces/bank-service.interface.js +3 -0
  87. package/dist/banking/interfaces/bank-service.interface.js.map +1 -0
  88. package/dist/banking/services/ASPAC.service.d.ts +25 -0
  89. package/dist/banking/services/ASPAC.service.d.ts.map +1 -0
  90. package/dist/banking/services/ASPAC.service.js +498 -0
  91. package/dist/banking/services/ASPAC.service.js.map +1 -0
  92. package/dist/banking/services/BankGateway.service.d.ts +18 -0
  93. package/dist/banking/services/BankGateway.service.d.ts.map +1 -0
  94. package/dist/banking/services/BankGateway.service.js +71 -0
  95. package/dist/banking/services/BankGateway.service.js.map +1 -0
  96. package/dist/banking/services/MBWIN.service.d.ts +25 -0
  97. package/dist/banking/services/MBWIN.service.d.ts.map +1 -0
  98. package/dist/banking/services/MBWIN.service.js +655 -0
  99. package/dist/banking/services/MBWIN.service.js.map +1 -0
  100. package/dist/banking/services/WelcomeBank.service.d.ts +29 -0
  101. package/dist/banking/services/WelcomeBank.service.d.ts.map +1 -0
  102. package/dist/banking/services/WelcomeBank.service.js +840 -0
  103. package/dist/banking/services/WelcomeBank.service.js.map +1 -0
  104. package/dist/banking/services/sms.service.d.ts +10 -0
  105. package/dist/banking/services/sms.service.d.ts.map +1 -0
  106. package/dist/banking/services/sms.service.js +97 -0
  107. package/dist/banking/services/sms.service.js.map +1 -0
  108. package/dist/banking/tokens/bank-service.tokens.d.ts +5 -0
  109. package/dist/banking/tokens/bank-service.tokens.d.ts.map +1 -0
  110. package/dist/banking/tokens/bank-service.tokens.js +8 -0
  111. package/dist/banking/tokens/bank-service.tokens.js.map +1 -0
  112. package/dist/banking/types.d.ts +10 -0
  113. package/dist/banking/types.d.ts.map +1 -0
  114. package/dist/banking/types.js +3 -0
  115. package/dist/banking/types.js.map +1 -0
  116. package/dist/decorators/response_message.decorator.d.ts +2 -0
  117. package/dist/decorators/response_message.decorator.d.ts.map +1 -0
  118. package/dist/decorators/response_message.decorator.js +7 -0
  119. package/dist/decorators/response_message.decorator.js.map +1 -0
  120. package/dist/helpers/axios-error.helper.d.ts +6 -0
  121. package/dist/helpers/axios-error.helper.d.ts.map +1 -0
  122. package/dist/helpers/axios-error.helper.js +78 -0
  123. package/dist/helpers/axios-error.helper.js.map +1 -0
  124. package/dist/helpers/response-format.helper.d.ts +4 -0
  125. package/dist/helpers/response-format.helper.d.ts.map +1 -0
  126. package/dist/helpers/response-format.helper.js +15 -0
  127. package/dist/helpers/response-format.helper.js.map +1 -0
  128. package/dist/index.d.ts +10 -0
  129. package/dist/index.d.ts.map +1 -0
  130. package/dist/index.js +26 -0
  131. package/dist/index.js.map +1 -0
  132. package/dist/interceptors/response.interceptor.d.ts +16 -0
  133. package/dist/interceptors/response.interceptor.d.ts.map +1 -0
  134. package/dist/interceptors/response.interceptor.js +80 -0
  135. package/dist/interceptors/response.interceptor.js.map +1 -0
  136. package/dist/logger/logger-interceptor.d.ts +14 -0
  137. package/dist/logger/logger-interceptor.d.ts.map +1 -0
  138. package/dist/logger/logger-interceptor.js +60 -0
  139. package/dist/logger/logger-interceptor.js.map +1 -0
  140. package/dist/logger/logger-option.d.ts +6 -0
  141. package/dist/logger/logger-option.d.ts.map +1 -0
  142. package/dist/logger/logger-option.js +103 -0
  143. package/dist/logger/logger-option.js.map +1 -0
  144. package/dist/logger/logger.config.d.ts +3 -0
  145. package/dist/logger/logger.config.d.ts.map +1 -0
  146. package/dist/logger/logger.config.js +31 -0
  147. package/dist/logger/logger.config.js.map +1 -0
  148. package/dist/logger/logger.module.d.ts +3 -0
  149. package/dist/logger/logger.module.d.ts.map +1 -0
  150. package/dist/logger/logger.module.js +76 -0
  151. package/dist/logger/logger.module.js.map +1 -0
  152. package/dist/main.d.ts +2 -0
  153. package/dist/main.d.ts.map +1 -0
  154. package/dist/main.js +13 -0
  155. package/dist/main.js.map +1 -0
  156. package/dist/tsconfig.build.tsbuildinfo +1 -0
  157. package/docker-compose.yml +14 -0
  158. package/ecosystem.config.js +35 -0
  159. package/eslint.config.mjs +34 -0
  160. package/nest-cli.json +8 -0
  161. package/package.json +102 -0
  162. package/src/app.module.ts +33 -0
  163. package/src/auth/apikey.guard.ts +42 -0
  164. package/src/auth/auth.controller.ts +41 -0
  165. package/src/auth/auth.interface.ts +7 -0
  166. package/src/auth/auth.module.ts +125 -0
  167. package/src/auth/constant.ts +1 -0
  168. package/src/auth/jwt-constant.ts +31 -0
  169. package/src/auth/jwt-guard.ts +5 -0
  170. package/src/auth/jwt-strategy.ts +43 -0
  171. package/src/auth/services/auth.service.ts +33 -0
  172. package/src/auth/services/encryption.service.ts +35 -0
  173. package/src/auth.config.ts +42 -0
  174. package/src/banking/banking.controller.ts +142 -0
  175. package/src/banking/banking.module.ts +142 -0
  176. package/src/banking/dto/account-transfer.dto.ts +66 -0
  177. package/src/banking/dto/account.dto.ts +70 -0
  178. package/src/banking/dto/fetch-bank-balance.dto.ts +46 -0
  179. package/src/banking/dto/transaction-history.dto.ts +58 -0
  180. package/src/banking/factory/bank-strategy.factory.ts +25 -0
  181. package/src/banking/interfaces/bank-config.interface.ts +25 -0
  182. package/src/banking/interfaces/bank-service.interface.ts +164 -0
  183. package/src/banking/services/ASPAC.service.ts +574 -0
  184. package/src/banking/services/BankGateway.service.ts +64 -0
  185. package/src/banking/services/MBWIN.service.ts +779 -0
  186. package/src/banking/services/WelcomeBank.service.ts +990 -0
  187. package/src/banking/services/sms.service.ts +50 -0
  188. package/src/banking/tokens/bank-service.tokens.ts +4 -0
  189. package/src/banking/types.ts +10 -0
  190. package/src/decorators/response_message.decorator.ts +4 -0
  191. package/src/helpers/axios-error.helper.ts +63 -0
  192. package/src/helpers/response-format.helper.ts +15 -0
  193. package/src/index.ts +15 -0
  194. package/src/interceptors/response.interceptor.ts +53 -0
  195. package/src/logger/logger-interceptor.ts +55 -0
  196. package/src/logger/logger-option.ts +119 -0
  197. package/src/logger/logger.config.ts +31 -0
  198. package/src/logger/logger.module.ts +31 -0
  199. package/src/main.ts +10 -0
  200. package/src/types/pino-daily-rotate-file.d.ts +31 -0
  201. package/tsconfig.build.json +11 -0
  202. package/tsconfig.build.tsbuildinfo +1 -0
  203. 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,4 @@
1
+ export const ASPAC_SERVICE_TOKEN = Symbol('ASPAC_SERVICE');
2
+ export const WELCOMEBANK_SERVICE_TOKEN = Symbol('WELCOMEBANK_SERVICE');
3
+ export const MBWIN_SERVICE_TOKEN = Symbol('MBWIN_SERVICE');
4
+ export const BANKING_CONFIG = Symbol('BANKING_CONFIG');
@@ -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,4 @@
1
+ import { SetMetadata } from '@nestjs/common';
2
+
3
+ export const ResponseMessage = (message: string) =>
4
+ SetMetadata('response_message', message);
@@ -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
+ }
@@ -0,0 +1,15 @@
1
+ export class ResponseFormatHelper {
2
+ static createResponse(
3
+ statusCode: number,
4
+ timestamp: string = new Date().toISOString(),
5
+ message: string,
6
+ data: any,
7
+ ): any {
8
+ return {
9
+ statusCode,
10
+ timestamp,
11
+ message,
12
+ data,
13
+ };
14
+ }
15
+ }
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
+ }
@@ -0,0 +1,11 @@
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "compilerOptions": {
4
+ "outDir": "./dist",
5
+ "declaration": true,
6
+ "declarationMap": true,
7
+ "sourceMap": true,
8
+ "removeComments": true
9
+ },
10
+ "include": ["src/**/*.ts"]
11
+ }