@ecashlib/shared-message 1.0.1 → 1.0.3

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.
@@ -3,61 +3,41 @@ import { IRedisCache } from "./cache-interfaces";
3
3
  * Redis Cache Adapter for Message System
4
4
  * Handles caching of messages with TTL support
5
5
  *
6
- * @example
7
- * import { MessageCache, IRedisCache } from "@ecashlib/shared-message";
8
- *
9
- * class YourRedisAdapter implements IRedisCache {
10
- * // implement methods using your redis wrapper
11
- * }
12
- *
13
- * const adapter = new YourRedisAdapter();
14
- * const cache = new MessageCache(adapter, 3600);
6
+ * Cache format (matches ecash-cms):
7
+ * - Key: `message:${code}` (Redis hash)
8
+ * - Fields: language codes (vi, en, ko, etc.)
9
+ * - Values: message content
15
10
  */
16
11
  export declare class MessageCache {
17
12
  private readonly redis;
18
13
  private readonly defaultTTL;
19
14
  constructor(redis: IRedisCache, defaultTTL?: number);
20
15
  /**
21
- * Get message content from cache
22
- * @param code - Message code (e.g., "ECASH_00001")
23
- * @param language - Language code (e.g., "vi", "en")
24
- * @returns Message content or null if not found
16
+ * Get message content from cache (hash field)
25
17
  */
26
18
  get(code: string, language: string): Promise<string | null>;
27
19
  /**
28
- * Get all languages for a message code from cache
29
- * @param code - Message code
30
- * @returns Object with all language translations
20
+ * Get all languages for a message code from cache (entire hash)
31
21
  */
32
22
  getAll(code: string): Promise<Record<string, string> | null>;
33
23
  /**
34
- * Set message content to cache with TTL
35
- * @param code - Message code
36
- * @param language - Language code
37
- * @param content - Message content
38
- * @param ttl - Time to live in seconds (optional, uses default)
24
+ * Set message content to cache (hash field)
39
25
  */
40
26
  set(code: string, language: string, content: string, ttl?: number): Promise<void>;
41
27
  /**
42
- * Set all languages for a message code
43
- * @param code - Message code
44
- * @param content - Object with all language translations
45
- * @param ttl - Time to live in seconds (optional)
28
+ * Set all languages for a message code (entire hash)
46
29
  */
47
30
  setAll(code: string, content: Record<string, string>, ttl?: number): Promise<void>;
48
31
  /**
49
- * Delete message from cache (all languages)
50
- * @param code - Message code
32
+ * Delete message from cache (all languages - delete hash)
51
33
  */
52
34
  delete(code: string): Promise<void>;
53
35
  /**
54
- * Delete specific language from cache
55
- * @param code - Message code
56
- * @param language - Language code
36
+ * Delete specific language from cache (delete hash field)
57
37
  */
58
38
  deleteLanguage(code: string, language: string): Promise<void>;
59
39
  /**
60
- * Clear all message cache (use with caution!)
40
+ * Clear all message cache
61
41
  */
62
42
  flush(): Promise<void>;
63
43
  /**
@@ -65,11 +45,7 @@ export declare class MessageCache {
65
45
  */
66
46
  exists(code: string, language: string): Promise<boolean>;
67
47
  /**
68
- * Helper to delete keys by pattern
69
- */
70
- private delPattern;
71
- /**
72
- * Generate cache key
48
+ * Generate cache key (hash format like ecash-cms)
73
49
  */
74
50
  private getKey;
75
51
  }
@@ -1 +1 @@
1
- {"version":3,"file":"message-cache.d.ts","sourceRoot":"","sources":["../../src/cache/message-cache.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEjD;;;;;;;;;;;;;GAaG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAc;IACpC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;gBAExB,KAAK,EAAE,WAAW,EAAE,UAAU,GAAE,MAAa;IAKzD;;;;;OAKG;IACG,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAMjE;;;;OAIG;IACG,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAAC;IAuBlE;;;;;;OAMG;IACG,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAKvF;;;;;OAKG;IACG,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAiBxF;;;OAGG;IACG,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAKzC;;;;OAIG;IACG,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAKnE;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAK5B;;OAEG;IACG,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAK9D;;OAEG;YACW,UAAU;IAUxB;;OAEG;IACH,OAAO,CAAC,MAAM;CAGf"}
1
+ {"version":3,"file":"message-cache.d.ts","sourceRoot":"","sources":["../../src/cache/message-cache.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEjD;;;;;;;;GAQG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAc;IACpC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;gBAExB,KAAK,EAAE,WAAW,EAAE,UAAU,GAAE,MAAa;IAKzD;;OAEG;IACG,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IASjE;;OAEG;IACG,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAAC;IASlE;;OAEG;IACG,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAKvF;;OAEG;IACG,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAOxF;;OAEG;IACG,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAKzC;;OAEG;IACG,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAKnE;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAK5B;;OAEG;IACG,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAM9D;;OAEG;IACH,OAAO,CAAC,MAAM;CAGf"}
@@ -5,15 +5,10 @@ exports.MessageCache = void 0;
5
5
  * Redis Cache Adapter for Message System
6
6
  * Handles caching of messages with TTL support
7
7
  *
8
- * @example
9
- * import { MessageCache, IRedisCache } from "@ecashlib/shared-message";
10
- *
11
- * class YourRedisAdapter implements IRedisCache {
12
- * // implement methods using your redis wrapper
13
- * }
14
- *
15
- * const adapter = new YourRedisAdapter();
16
- * const cache = new MessageCache(adapter, 3600);
8
+ * Cache format (matches ecash-cms):
9
+ * - Key: `message:${code}` (Redis hash)
10
+ * - Fields: language codes (vi, en, ko, etc.)
11
+ * - Values: message content
17
12
  */
18
13
  class MessageCache {
19
14
  constructor(redis, defaultTTL = 3600) {
@@ -21,117 +16,77 @@ class MessageCache {
21
16
  this.defaultTTL = defaultTTL;
22
17
  }
23
18
  /**
24
- * Get message content from cache
25
- * @param code - Message code (e.g., "ECASH_00001")
26
- * @param language - Language code (e.g., "vi", "en")
27
- * @returns Message content or null if not found
19
+ * Get message content from cache (hash field)
28
20
  */
29
21
  async get(code, language) {
30
- const key = this.getKey(code, language);
31
- const cached = await this.redis.get(key);
32
- return cached;
22
+ const key = this.getKey(code);
23
+ const hash = await this.redis.hgetall(key);
24
+ if (hash && hash[language]) {
25
+ return hash[language];
26
+ }
27
+ return null;
33
28
  }
34
29
  /**
35
- * Get all languages for a message code from cache
36
- * @param code - Message code
37
- * @returns Object with all language translations
30
+ * Get all languages for a message code from cache (entire hash)
38
31
  */
39
32
  async getAll(code) {
40
- const pattern = `message:${code}:*`;
41
- const keys = await this.delPattern(pattern);
42
- if (keys.length === 0) {
43
- return null;
44
- }
45
- const result = {};
46
- for (const key of keys) {
47
- const value = await this.redis.get(key);
48
- if (value) {
49
- // Extract language from key: "message:ECASH_00001:vi" -> "vi"
50
- const language = key.split(":").pop();
51
- if (language) {
52
- result[language] = value;
53
- }
54
- }
33
+ const key = this.getKey(code);
34
+ const hash = await this.redis.hgetall(key);
35
+ if (hash && Object.keys(hash).length > 0) {
36
+ return hash;
55
37
  }
56
- return Object.keys(result).length > 0 ? result : null;
38
+ return null;
57
39
  }
58
40
  /**
59
- * Set message content to cache with TTL
60
- * @param code - Message code
61
- * @param language - Language code
62
- * @param content - Message content
63
- * @param ttl - Time to live in seconds (optional, uses default)
41
+ * Set message content to cache (hash field)
64
42
  */
65
43
  async set(code, language, content, ttl) {
66
- const key = this.getKey(code, language);
67
- await this.redis.set(key, content, ttl ?? this.defaultTTL);
44
+ const key = this.getKey(code);
45
+ await this.redis.hset(key, language, content);
68
46
  }
69
47
  /**
70
- * Set all languages for a message code
71
- * @param code - Message code
72
- * @param content - Object with all language translations
73
- * @param ttl - Time to live in seconds (optional)
48
+ * Set all languages for a message code (entire hash)
74
49
  */
75
50
  async setAll(code, content, ttl) {
76
- const expireTtl = ttl ?? this.defaultTTL;
77
- const keyValues = {};
51
+ const key = this.getKey(code);
78
52
  for (const [language, message] of Object.entries(content)) {
79
- const key = this.getKey(code, language);
80
- keyValues[key] = message;
81
- }
82
- await this.redis.mset(keyValues);
83
- // Set TTL for each key individually since mset doesn't support TTL
84
- for (const key of Object.keys(keyValues)) {
85
- await this.redis.set(key, keyValues[key], expireTtl);
53
+ await this.redis.hset(key, language, message);
86
54
  }
87
55
  }
88
56
  /**
89
- * Delete message from cache (all languages)
90
- * @param code - Message code
57
+ * Delete message from cache (all languages - delete hash)
91
58
  */
92
59
  async delete(code) {
93
- const pattern = `message:${code}:*`;
94
- await this.delPattern(pattern);
60
+ const key = this.getKey(code);
61
+ await this.redis.hdel(key);
95
62
  }
96
63
  /**
97
- * Delete specific language from cache
98
- * @param code - Message code
99
- * @param language - Language code
64
+ * Delete specific language from cache (delete hash field)
100
65
  */
101
66
  async deleteLanguage(code, language) {
102
- const key = this.getKey(code, language);
103
- await this.redis.del(key);
67
+ const key = this.getKey(code);
68
+ await this.redis.hdel(key, language);
104
69
  }
105
70
  /**
106
- * Clear all message cache (use with caution!)
71
+ * Clear all message cache
107
72
  */
108
73
  async flush() {
109
74
  const pattern = "message:*";
110
- await this.delPattern(pattern);
75
+ await this.redis.delPattern(pattern);
111
76
  }
112
77
  /**
113
78
  * Check if message exists in cache
114
79
  */
115
80
  async exists(code, language) {
116
- const key = this.getKey(code, language);
117
- return await this.redis.exists(key);
118
- }
119
- /**
120
- * Helper to delete keys by pattern
121
- */
122
- async delPattern(pattern) {
123
- // This requires the adapter to implement delPattern properly
124
- // For fastify.redis, keys() returns array, then we call del()
125
- const keys = [];
126
- // We need to scan for keys matching pattern
127
- // Since IRedisCache.delPattern returns the deleted keys, we use it
128
- return await this.redis.delPattern(pattern);
81
+ const key = this.getKey(code);
82
+ const hash = await this.redis.hgetall(key);
83
+ return hash !== undefined && hash[language] !== undefined;
129
84
  }
130
85
  /**
131
- * Generate cache key
86
+ * Generate cache key (hash format like ecash-cms)
132
87
  */
133
- getKey(code, language) {
134
- return `message:${code}:${language}`;
88
+ getKey(code) {
89
+ return `message:${code}`;
135
90
  }
136
91
  }
137
92
  exports.MessageCache = MessageCache;
@@ -0,0 +1,50 @@
1
+ import { FastifyInstance } from "fastify";
2
+ import { MessageUtil } from "./message.util";
3
+ /**
4
+ * Response interfaces
5
+ */
6
+ export interface PopupNotification {
7
+ title: string;
8
+ left: string | null;
9
+ middle: string | null;
10
+ right: string | null;
11
+ count: number;
12
+ }
13
+ export interface ErrorResponse {
14
+ success: false;
15
+ code: string;
16
+ message: string;
17
+ data: null;
18
+ metadata: null;
19
+ popup: PopupNotification | null;
20
+ }
21
+ /**
22
+ * Custom Error Classes
23
+ */
24
+ export declare class BusinessLogicError extends Error {
25
+ code: string;
26
+ language: string;
27
+ constructor(code: string, language?: string);
28
+ }
29
+ export declare class ValidationError extends Error {
30
+ details?: any | undefined;
31
+ constructor(message: string, details?: any | undefined);
32
+ }
33
+ export declare class NotFoundError extends Error {
34
+ constructor(message?: string);
35
+ }
36
+ export declare class ConflictError extends Error {
37
+ constructor(message?: string);
38
+ }
39
+ /**
40
+ * Factory function to create error handler plugin
41
+ */
42
+ export declare function createErrorHandlerPlugin(messageUtil: MessageUtil): (fastify: FastifyInstance) => Promise<void>;
43
+ /**
44
+ * Helper functions to throw standardized errors
45
+ */
46
+ export declare const throwBusinessLogicError: (code: string, language?: string) => never;
47
+ export declare const throwValidationError: (message: string, details?: any) => never;
48
+ export declare const throwNotFoundError: (message?: string) => never;
49
+ export declare const throwConflictError: (message?: string) => never;
50
+ //# sourceMappingURL=error-handler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"error-handler.d.ts","sourceRoot":"","sources":["../src/error-handler.ts"],"names":[],"mappings":"AACA,OAAO,EAEH,eAAe,EAGlB,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAE7C;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,aAAa;IAC1B,OAAO,EAAE,KAAK,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,IAAI,CAAC;IACX,QAAQ,EAAE,IAAI,CAAC;IACf,KAAK,EAAE,iBAAiB,GAAG,IAAI,CAAC;CACnC;AAED;;GAEG;AACH,qBAAa,kBAAmB,SAAQ,KAAK;IAE9B,IAAI,EAAE,MAAM;IACZ,QAAQ,EAAE,MAAM;gBADhB,IAAI,EAAE,MAAM,EACZ,QAAQ,GAAE,MAAa;CAKrC;AAED,qBAAa,eAAgB,SAAQ,KAAK;IAG3B,OAAO,CAAC,EAAE,GAAG;gBADpB,OAAO,EAAE,MAAM,EACR,OAAO,CAAC,EAAE,GAAG,YAAA;CAK3B;AAED,qBAAa,aAAc,SAAQ,KAAK;gBACxB,OAAO,GAAE,MAA6B;CAIrD;AAED,qBAAa,aAAc,SAAQ,KAAK;gBACxB,OAAO,GAAE,MAA4B;CAIpD;AAyCD;;GAEG;AACH,wBAAgB,wBAAwB,CAAC,WAAW,EAAE,WAAW,aAEpB,eAAe,mBAmK3D;AAED;;GAEG;AACH,eAAO,MAAM,uBAAuB,GAChC,MAAM,MAAM,EACZ,WAAU,MAAa,KACxB,KAEF,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAAI,SAAS,MAAM,EAAE,UAAU,GAAG,KAAG,KAErE,CAAC;AAEF,eAAO,MAAM,kBAAkB,GAAI,UAAU,MAAM,KAAG,KAErD,CAAC;AAEF,eAAO,MAAM,kBAAkB,GAAI,UAAU,MAAM,KAAG,KAErD,CAAC"}
@@ -0,0 +1,249 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.throwConflictError = exports.throwNotFoundError = exports.throwValidationError = exports.throwBusinessLogicError = exports.ConflictError = exports.NotFoundError = exports.ValidationError = exports.BusinessLogicError = void 0;
7
+ exports.createErrorHandlerPlugin = createErrorHandlerPlugin;
8
+ const fastify_plugin_1 = __importDefault(require("fastify-plugin"));
9
+ /**
10
+ * Custom Error Classes
11
+ */
12
+ class BusinessLogicError extends Error {
13
+ constructor(code, language = "vi") {
14
+ super(code); // Use code as temporary message
15
+ this.code = code;
16
+ this.language = language;
17
+ this.name = "BusinessLogicError";
18
+ }
19
+ }
20
+ exports.BusinessLogicError = BusinessLogicError;
21
+ class ValidationError extends Error {
22
+ constructor(message, details) {
23
+ super(message);
24
+ this.details = details;
25
+ this.name = "ValidationError";
26
+ }
27
+ }
28
+ exports.ValidationError = ValidationError;
29
+ class NotFoundError extends Error {
30
+ constructor(message = "Resource not found") {
31
+ super(message);
32
+ this.name = "NotFoundError";
33
+ }
34
+ }
35
+ exports.NotFoundError = NotFoundError;
36
+ class ConflictError extends Error {
37
+ constructor(message = "Resource conflict") {
38
+ super(message);
39
+ this.name = "ConflictError";
40
+ }
41
+ }
42
+ exports.ConflictError = ConflictError;
43
+ /**
44
+ * Default popup configurations
45
+ */
46
+ const DEFAULT_POPUPS = {
47
+ ERROR: {
48
+ title: "Error",
49
+ left: null,
50
+ middle: null,
51
+ right: "OK",
52
+ count: 1
53
+ },
54
+ WARNING: {
55
+ title: "Warning",
56
+ left: "Cancel",
57
+ middle: null,
58
+ right: "OK",
59
+ count: 2
60
+ },
61
+ INFO: {
62
+ title: "Information",
63
+ left: null,
64
+ middle: null,
65
+ right: "OK",
66
+ count: 1
67
+ }
68
+ };
69
+ /**
70
+ * Default response codes
71
+ */
72
+ const RESPONSE_CODES = {
73
+ SUCCESS: "COMM0200",
74
+ BAD_REQUEST: "COMM0002",
75
+ NOT_FOUND: "COMM0404",
76
+ CONFLICT: "COMM0409",
77
+ VALIDATION_ERROR: "COMM0002",
78
+ SYSTEM_ERROR: "COMM0001"
79
+ };
80
+ /**
81
+ * Factory function to create error handler plugin
82
+ */
83
+ function createErrorHandlerPlugin(messageUtil) {
84
+ return (0, fastify_plugin_1.default)(async function errorHandler(fastify) {
85
+ // Set error handler
86
+ fastify.setErrorHandler(async (error, request, reply) => {
87
+ // Log error for debugging
88
+ fastify.log.error({
89
+ error: error.message,
90
+ stack: error.stack,
91
+ url: request.url,
92
+ method: request.method,
93
+ params: request.params,
94
+ query: request.query,
95
+ body: request.body
96
+ }, "Request error occurred");
97
+ let response;
98
+ const statusCode = 200; // Always return HTTP 200
99
+ // Handle different types of errors
100
+ if (error instanceof BusinessLogicError) {
101
+ // Fetch message and popup from CMS via MessageUtil
102
+ const messageContent = await messageUtil.getMessageContentByCode(error.code, error.language);
103
+ if (messageContent) {
104
+ response = {
105
+ success: false,
106
+ code: error.code,
107
+ message: messageContent.message,
108
+ data: null,
109
+ metadata: null,
110
+ popup: messageContent.popup || DEFAULT_POPUPS.ERROR
111
+ };
112
+ }
113
+ else {
114
+ // Fallback if message not found in CMS
115
+ fastify.log.warn(`Message not found for code: ${error.code}, language: ${error.language}`);
116
+ response = {
117
+ success: false,
118
+ code: error.code,
119
+ message: `Error: ${error.code}`,
120
+ data: null,
121
+ metadata: null,
122
+ popup: DEFAULT_POPUPS.ERROR
123
+ };
124
+ }
125
+ }
126
+ else if (error instanceof ValidationError) {
127
+ response = {
128
+ success: false,
129
+ code: RESPONSE_CODES.VALIDATION_ERROR,
130
+ message: error.message,
131
+ data: null,
132
+ metadata: null,
133
+ popup: DEFAULT_POPUPS.WARNING
134
+ };
135
+ }
136
+ else if (error instanceof NotFoundError) {
137
+ response = {
138
+ success: false,
139
+ code: RESPONSE_CODES.NOT_FOUND,
140
+ message: error.message,
141
+ data: null,
142
+ metadata: null,
143
+ popup: null
144
+ };
145
+ }
146
+ else if (error instanceof ConflictError) {
147
+ response = {
148
+ success: false,
149
+ code: RESPONSE_CODES.CONFLICT,
150
+ message: error.message,
151
+ data: null,
152
+ metadata: null,
153
+ popup: DEFAULT_POPUPS.WARNING
154
+ };
155
+ }
156
+ else if (error.validation) {
157
+ // Fastify validation errors
158
+ const validationMessage = error.validation
159
+ .map((err) => `${err.instancePath || err.schemaPath}: ${err.message}`)
160
+ .join(", ");
161
+ response = {
162
+ success: false,
163
+ code: RESPONSE_CODES.VALIDATION_ERROR,
164
+ message: `Validation failed: ${validationMessage}`,
165
+ data: null,
166
+ metadata: null,
167
+ popup: null
168
+ };
169
+ }
170
+ else if (error.statusCode === 404) {
171
+ response = {
172
+ success: false,
173
+ code: RESPONSE_CODES.NOT_FOUND,
174
+ message: "Endpoint not found",
175
+ data: null,
176
+ metadata: null,
177
+ popup: null
178
+ };
179
+ }
180
+ else if (error.statusCode === 429) {
181
+ response = {
182
+ success: false,
183
+ code: RESPONSE_CODES.BAD_REQUEST,
184
+ message: "Too many requests, please try again later.",
185
+ data: null,
186
+ metadata: null,
187
+ popup: DEFAULT_POPUPS.WARNING
188
+ };
189
+ }
190
+ else if (error.statusCode && error.statusCode < 500) {
191
+ // Client errors (4xx)
192
+ response = {
193
+ success: false,
194
+ code: RESPONSE_CODES.BAD_REQUEST,
195
+ message: error.message || "Bad request",
196
+ data: null,
197
+ metadata: null,
198
+ popup: null
199
+ };
200
+ }
201
+ else {
202
+ // Server errors (5xx) or unknown errors
203
+ response = {
204
+ success: false,
205
+ code: RESPONSE_CODES.SYSTEM_ERROR,
206
+ message: "An unexpected error occurred",
207
+ data: null,
208
+ metadata: null,
209
+ popup: DEFAULT_POPUPS.ERROR
210
+ };
211
+ }
212
+ // Send response - always HTTP 200
213
+ reply.status(statusCode).send(response);
214
+ });
215
+ // Handle 404 for routes that don't exist
216
+ fastify.setNotFoundHandler(async (request, reply) => {
217
+ const response = {
218
+ success: false,
219
+ code: RESPONSE_CODES.NOT_FOUND,
220
+ message: `Route ${request.method} ${request.url} not found`,
221
+ data: null,
222
+ metadata: null,
223
+ popup: null
224
+ };
225
+ reply.status(200).send(response);
226
+ });
227
+ }, {
228
+ name: "shared-error-handler"
229
+ });
230
+ }
231
+ /**
232
+ * Helper functions to throw standardized errors
233
+ */
234
+ const throwBusinessLogicError = (code, language = "vi") => {
235
+ throw new BusinessLogicError(code, language);
236
+ };
237
+ exports.throwBusinessLogicError = throwBusinessLogicError;
238
+ const throwValidationError = (message, details) => {
239
+ throw new ValidationError(message, details);
240
+ };
241
+ exports.throwValidationError = throwValidationError;
242
+ const throwNotFoundError = (message) => {
243
+ throw new NotFoundError(message);
244
+ };
245
+ exports.throwNotFoundError = throwNotFoundError;
246
+ const throwConflictError = (message) => {
247
+ throw new ConflictError(message);
248
+ };
249
+ exports.throwConflictError = throwConflictError;
package/dist/index.d.ts CHANGED
@@ -1,3 +1,9 @@
1
- export * from "./message";
2
- export * from "./cache";
1
+ export { MessageUtil } from "./message.util";
2
+ export { MessageCMSAdapter } from "./message-cms.adapter";
3
+ export { RedisRepository } from "./redis.repository";
4
+ export type { MessageContent, MessageServicePort } from "./message";
5
+ export { default as messageUtilPlugin } from "./message.util";
6
+ export { default as redisRepositoryPlugin } from "./redis.repository";
7
+ export { BusinessLogicError, ValidationError, NotFoundError, ConflictError, createErrorHandlerPlugin, throwBusinessLogicError, throwValidationError, throwNotFoundError, throwConflictError } from "./error-handler";
8
+ export type { PopupNotification, ErrorResponse } from "./error-handler";
3
9
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAC;AAC1B,cAAc,SAAS,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,YAAY,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAGpE,OAAO,EAAE,OAAO,IAAI,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAC9D,OAAO,EAAE,OAAO,IAAI,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAGtE,OAAO,EACH,kBAAkB,EAClB,eAAe,EACf,aAAa,EACb,aAAa,EACb,wBAAwB,EACxB,uBAAuB,EACvB,oBAAoB,EACpB,kBAAkB,EAClB,kBAAkB,EACrB,MAAM,iBAAiB,CAAC;AACzB,YAAY,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC"}
package/dist/index.js CHANGED
@@ -1,18 +1,28 @@
1
1
  "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
- for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
15
4
  };
16
5
  Object.defineProperty(exports, "__esModule", { value: true });
17
- __exportStar(require("./message"), exports);
18
- __exportStar(require("./cache"), exports);
6
+ exports.throwConflictError = exports.throwNotFoundError = exports.throwValidationError = exports.throwBusinessLogicError = exports.createErrorHandlerPlugin = exports.ConflictError = exports.NotFoundError = exports.ValidationError = exports.BusinessLogicError = exports.redisRepositoryPlugin = exports.messageUtilPlugin = exports.RedisRepository = exports.MessageCMSAdapter = exports.MessageUtil = void 0;
7
+ var message_util_1 = require("./message.util");
8
+ Object.defineProperty(exports, "MessageUtil", { enumerable: true, get: function () { return message_util_1.MessageUtil; } });
9
+ var message_cms_adapter_1 = require("./message-cms.adapter");
10
+ Object.defineProperty(exports, "MessageCMSAdapter", { enumerable: true, get: function () { return message_cms_adapter_1.MessageCMSAdapter; } });
11
+ var redis_repository_1 = require("./redis.repository");
12
+ Object.defineProperty(exports, "RedisRepository", { enumerable: true, get: function () { return redis_repository_1.RedisRepository; } });
13
+ // Fastify plugins (default exports)
14
+ var message_util_2 = require("./message.util");
15
+ Object.defineProperty(exports, "messageUtilPlugin", { enumerable: true, get: function () { return __importDefault(message_util_2).default; } });
16
+ var redis_repository_2 = require("./redis.repository");
17
+ Object.defineProperty(exports, "redisRepositoryPlugin", { enumerable: true, get: function () { return __importDefault(redis_repository_2).default; } });
18
+ // Error handling
19
+ var error_handler_1 = require("./error-handler");
20
+ Object.defineProperty(exports, "BusinessLogicError", { enumerable: true, get: function () { return error_handler_1.BusinessLogicError; } });
21
+ Object.defineProperty(exports, "ValidationError", { enumerable: true, get: function () { return error_handler_1.ValidationError; } });
22
+ Object.defineProperty(exports, "NotFoundError", { enumerable: true, get: function () { return error_handler_1.NotFoundError; } });
23
+ Object.defineProperty(exports, "ConflictError", { enumerable: true, get: function () { return error_handler_1.ConflictError; } });
24
+ Object.defineProperty(exports, "createErrorHandlerPlugin", { enumerable: true, get: function () { return error_handler_1.createErrorHandlerPlugin; } });
25
+ Object.defineProperty(exports, "throwBusinessLogicError", { enumerable: true, get: function () { return error_handler_1.throwBusinessLogicError; } });
26
+ Object.defineProperty(exports, "throwValidationError", { enumerable: true, get: function () { return error_handler_1.throwValidationError; } });
27
+ Object.defineProperty(exports, "throwNotFoundError", { enumerable: true, get: function () { return error_handler_1.throwNotFoundError; } });
28
+ Object.defineProperty(exports, "throwConflictError", { enumerable: true, get: function () { return error_handler_1.throwConflictError; } });
@@ -1 +1 @@
1
- {"version":3,"file":"message-helper.d.ts","sourceRoot":"","sources":["../../src/message/message-helper.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAE5D;;;;;;;;;;;;;GAaG;AACH,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,cAAc,EACvB,WAAW,GAAE,MAAa,GACzB,MAAM,CAgBR;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,UAAU,CAC9B,cAAc,EAAE,kBAAkB,EAClC,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,MAAM,CAAC,EAAE,aAAa,GACrB,OAAO,CAAC,MAAM,CAAC,CA6BjB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,MAAM,EAChB,MAAM,CAAC,EAAE,aAAa,GACrB,MAAM,CAeR;AAED;;;;;;;;GAQG;AACH,wBAAsB,qBAAqB,CACzC,OAAO,EAAE,cAAc,EACvB,cAAc,EAAE,kBAAkB,EAClC,IAAI,EAAE,MAAM,EACZ,MAAM,CAAC,EAAE,aAAa,GACrB,OAAO,CAAC,MAAM,CAAC,CAGjB"}
1
+ {"version":3,"file":"message-helper.d.ts","sourceRoot":"","sources":["../../src/message/message-helper.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAE5D;;;;;;;;;;;;;GAaG;AACH,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,cAAc,EACvB,WAAW,GAAE,MAAa,GACzB,MAAM,CAgBR;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,UAAU,CAC9B,cAAc,EAAE,kBAAkB,EAClC,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,MAAM,CAAC,EAAE,aAAa,GACrB,OAAO,CAAC,MAAM,CAAC,CA8BjB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,MAAM,EAChB,MAAM,CAAC,EAAE,aAAa,GACrB,MAAM,CAeR;AAED;;;;;;;;GAQG;AACH,wBAAsB,qBAAqB,CACzC,OAAO,EAAE,cAAc,EACvB,cAAc,EAAE,kBAAkB,EAClC,IAAI,EAAE,MAAM,EACZ,MAAM,CAAC,EAAE,aAAa,GACrB,OAAO,CAAC,MAAM,CAAC,CAGjB"}
@@ -65,19 +65,17 @@ async function getMessage(messageService, code, language, params) {
65
65
  console.error(`Error getting message ${code} in ${language}:`, error);
66
66
  }
67
67
  // Fallback to Vietnamese if available
68
- if (language !== "vi") {
69
- try {
70
- const fallback = await messageService.getMessageContentByCode(code, "vi");
71
- if (fallback?.message) {
72
- return formatMessage(fallback.message, params);
73
- }
74
- }
75
- catch (error) {
76
- console.error(`Error getting fallback message ${code}:`, error);
68
+ try {
69
+ const fallback = await messageService.getMessageContentByCode("ECASH_0000", language);
70
+ if (fallback?.message) {
71
+ return formatMessage(fallback.message, params);
77
72
  }
78
73
  }
74
+ catch (error) {
75
+ console.error(`Error getting fallback message ${code}:`, error);
76
+ }
79
77
  // Final fallback - return code
80
- return `[${code}]`;
78
+ return `Có lỗi xẩy ra, vui lòng thử lại sau.`;
81
79
  }
82
80
  /**
83
81
  * Replace {key} placeholders in message template with actual values