@ecashlib/shared-message 1.0.2 → 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.
@@ -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; } });
@@ -0,0 +1,17 @@
1
+ import { MessageContent } from "./message";
2
+ /**
3
+ * Adapter for fetching messages from CMS API
4
+ */
5
+ export declare class MessageCMSAdapter {
6
+ private readonly cmsApiUrl;
7
+ private readonly cmsContextPath;
8
+ constructor();
9
+ /**
10
+ * Fetch message content from CMS API
11
+ * @param code Message code
12
+ * @param language Language code
13
+ * @returns Message content or null if not found
14
+ */
15
+ getMessageContentByCode(code: string, language: string): Promise<MessageContent | null>;
16
+ }
17
+ //# sourceMappingURL=message-cms.adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"message-cms.adapter.d.ts","sourceRoot":"","sources":["../src/message-cms.adapter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAuB3C;;GAEG;AACH,qBAAa,iBAAiB;IAC1B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;;IAOxC;;;;;OAKG;IACG,uBAAuB,CACzB,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,GACjB,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC;CAyCpC"}
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MessageCMSAdapter = void 0;
4
+ /**
5
+ * Adapter for fetching messages from CMS API
6
+ */
7
+ class MessageCMSAdapter {
8
+ constructor() {
9
+ this.cmsApiUrl = process.env.CMS_API_URL || "";
10
+ this.cmsContextPath = process.env.CMS_CONTEXT_PATH || "";
11
+ }
12
+ /**
13
+ * Fetch message content from CMS API
14
+ * @param code Message code
15
+ * @param language Language code
16
+ * @returns Message content or null if not found
17
+ */
18
+ async getMessageContentByCode(code, language) {
19
+ const fullUrl = `${this.cmsApiUrl}${this.cmsContextPath}/api/v1/messages/${encodeURIComponent(code)}/content/${encodeURIComponent(language)}`;
20
+ try {
21
+ const response = await fetch(fullUrl, {
22
+ method: "GET",
23
+ headers: {
24
+ "Content-Type": "application/json"
25
+ }
26
+ });
27
+ // Handle 404 - message not found
28
+ if (response.status === 404) {
29
+ return null;
30
+ }
31
+ if (!response.ok) {
32
+ const errorData = await response.json();
33
+ throw new Error(`CMS API error: ${errorData.message || response.statusText}`);
34
+ }
35
+ const data = await response.json();
36
+ if (!data.success || !data.data) {
37
+ return null;
38
+ }
39
+ return {
40
+ code: data.data.code,
41
+ classificationCode: data.data.classificationCode,
42
+ message: data.data.message,
43
+ popup: data.data.popup
44
+ };
45
+ }
46
+ catch (error) {
47
+ throw new Error(`Không thể lấy thông tin message từ CMS API: ${error.message}`);
48
+ }
49
+ }
50
+ }
51
+ exports.MessageCMSAdapter = MessageCMSAdapter;
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Message content structure returned from CMS API
3
+ */
4
+ export interface MessagePopup {
5
+ title: string;
6
+ left: string | null;
7
+ middle: string | null;
8
+ right: string | null;
9
+ count: number;
10
+ }
11
+ export interface MessageContent {
12
+ code: string;
13
+ classificationCode: string;
14
+ message: string;
15
+ popup?: MessagePopup;
16
+ }
17
+ /**
18
+ * Port interface for message service implementations
19
+ * This allows different implementations (direct API, cached, mock, etc.)
20
+ */
21
+ export interface MessageServicePort {
22
+ /**
23
+ * Get message content by code and language
24
+ * @param code Message code
25
+ * @param language Language code (e.g., 'vi', 'en')
26
+ * @returns Message content or null if not found
27
+ */
28
+ getMessageContentByCode(code: string, language: string): Promise<MessageContent | null>;
29
+ }
30
+ //# sourceMappingURL=message.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"message.d.ts","sourceRoot":"","sources":["../src/message.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,YAAY;IACzB,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,cAAc;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,kBAAkB,EAAE,MAAM,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,YAAY,CAAC;CACxB;AAED;;;GAGG;AACH,MAAM,WAAW,kBAAkB;IAC/B;;;;;OAKG;IACH,uBAAuB,CACnB,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,GACjB,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAAC;CACrC"}
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,35 @@
1
+ import { FastifyInstance } from "fastify";
2
+ import { MessageContent, MessageServicePort } from "./message";
3
+ declare module "fastify" {
4
+ interface FastifyInstance {
5
+ messageUtil: MessageUtil;
6
+ }
7
+ }
8
+ /**
9
+ * Message utility with cache-first strategy
10
+ * Checks Redis cache first, then calls CMS API if not cached
11
+ */
12
+ export declare class MessageUtil implements MessageServicePort {
13
+ private readonly redisRepository;
14
+ private readonly cmsAdapter;
15
+ private readonly logger;
16
+ private readonly defaultTTL;
17
+ constructor(fastify: FastifyInstance);
18
+ /**
19
+ * Get message content by code and language with cache-first strategy
20
+ * @param code Message code
21
+ * @param language Language code (e.g., 'vi', 'en')
22
+ * @returns Message content or null if not found
23
+ */
24
+ getMessageContentByCode(code: string, language: string): Promise<MessageContent | null>;
25
+ /**
26
+ * Invalidate cached message
27
+ * @param code Message code
28
+ * @param language Language code
29
+ * @returns true if cache was deleted
30
+ */
31
+ invalidateCache(code: string, language: string): Promise<boolean>;
32
+ }
33
+ declare const _default: (fastify: FastifyInstance) => Promise<void>;
34
+ export default _default;
35
+ //# sourceMappingURL=message.util.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"message.util.d.ts","sourceRoot":"","sources":["../src/message.util.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE1C,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAI/D,OAAO,QAAQ,SAAS,CAAC;IACrB,UAAU,eAAe;QACrB,WAAW,EAAE,WAAW,CAAC;KAC5B;CACJ;AAED;;;GAGG;AACH,qBAAa,WAAY,YAAW,kBAAkB;IAClD,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAkB;IAClD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAoB;IAC/C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAyB;IAChD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAgB;gBAE/B,OAAO,EAAE,eAAe;IAMpC;;;;;OAKG;IACG,uBAAuB,CACzB,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,GACjB,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC;IA4CjC;;;;;OAKG;IACG,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;CAgB1E;kCAGmB,eAAe;AADnC,wBAME"}
@@ -0,0 +1,75 @@
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.MessageUtil = void 0;
7
+ const fastify_plugin_1 = __importDefault(require("fastify-plugin"));
8
+ const message_cms_adapter_1 = require("./message-cms.adapter");
9
+ /**
10
+ * Message utility with cache-first strategy
11
+ * Checks Redis cache first, then calls CMS API if not cached
12
+ */
13
+ class MessageUtil {
14
+ constructor(fastify) {
15
+ this.defaultTTL = 3600; // 1 hour
16
+ this.redisRepository = fastify.redisRepository;
17
+ this.cmsAdapter = new message_cms_adapter_1.MessageCMSAdapter();
18
+ this.logger = fastify.log;
19
+ }
20
+ /**
21
+ * Get message content by code and language with cache-first strategy
22
+ * @param code Message code
23
+ * @param language Language code (e.g., 'vi', 'en')
24
+ * @returns Message content or null if not found
25
+ */
26
+ async getMessageContentByCode(code, language) {
27
+ const cacheKey = `message:${code}:${language}`;
28
+ try {
29
+ // Step 1: Check Redis cache
30
+ const cachedMessage = await this.redisRepository.getObject(cacheKey);
31
+ if (cachedMessage) {
32
+ this.logger.debug(`Message found in cache - code: ${code}, language: ${language}`);
33
+ return cachedMessage;
34
+ }
35
+ this.logger.debug(`Message not in cache, fetching from CMS - code: ${code}, language: ${language}`);
36
+ // Step 2: Fetch from CMS API
37
+ const messageContent = await this.cmsAdapter.getMessageContentByCode(code, language);
38
+ // Step 3: Cache the result if found
39
+ if (messageContent) {
40
+ await this.redisRepository.setObject(cacheKey, messageContent, this.defaultTTL);
41
+ this.logger.debug(`Message cached - code: ${code}, language: ${language}, ttl: ${this.defaultTTL}s`);
42
+ }
43
+ return messageContent;
44
+ }
45
+ catch (error) {
46
+ console.log(error);
47
+ this.logger.error(`Error getting message - code: ${code}, language: ${language}`, error);
48
+ // Return null on error to prevent breaking the application
49
+ return null;
50
+ }
51
+ }
52
+ /**
53
+ * Invalidate cached message
54
+ * @param code Message code
55
+ * @param language Language code
56
+ * @returns true if cache was deleted
57
+ */
58
+ async invalidateCache(code, language) {
59
+ const cacheKey = `message:${code}:${language}`;
60
+ try {
61
+ const deleted = await this.redisRepository.delete(cacheKey);
62
+ this.logger.debug(`Cache invalidated - code: ${code}, language: ${language}, deleted: ${deleted}`);
63
+ return deleted;
64
+ }
65
+ catch (error) {
66
+ this.logger.error(`Error invalidating cache - code: ${code}, language: ${language}`, error);
67
+ return false;
68
+ }
69
+ }
70
+ }
71
+ exports.MessageUtil = MessageUtil;
72
+ exports.default = (0, fastify_plugin_1.default)(async (fastify) => {
73
+ const util = new MessageUtil(fastify);
74
+ fastify.decorate("messageUtil", util);
75
+ }, { name: "message-util", dependencies: ["redis-repository"] });