@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.
- package/dist/cache/message-cache.d.ts +12 -36
- package/dist/cache/message-cache.d.ts.map +1 -1
- package/dist/cache/message-cache.js +37 -82
- package/dist/error-handler.d.ts +50 -0
- package/dist/error-handler.d.ts.map +1 -0
- package/dist/error-handler.js +249 -0
- package/dist/index.d.ts +8 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +25 -15
- package/dist/message/message-helper.d.ts.map +1 -1
- package/dist/message/message-helper.js +8 -10
- package/dist/message-cms.adapter.d.ts +17 -0
- package/dist/message-cms.adapter.d.ts.map +1 -0
- package/dist/message-cms.adapter.js +51 -0
- package/dist/message.d.ts +30 -0
- package/dist/message.d.ts.map +1 -0
- package/dist/message.js +2 -0
- package/dist/message.util.d.ts +35 -0
- package/dist/message.util.d.ts.map +1 -0
- package/dist/message.util.js +75 -0
- package/dist/redis.repository.d.ts +213 -0
- package/dist/redis.repository.d.ts.map +1 -0
- package/dist/redis.repository.js +619 -0
- package/package.json +40 -37
|
@@ -0,0 +1,619 @@
|
|
|
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.RedisHashMap = exports.RedisRepository = void 0;
|
|
7
|
+
const fastify_plugin_1 = __importDefault(require("fastify-plugin"));
|
|
8
|
+
const bigIntSerializer = (key, value) => {
|
|
9
|
+
if (typeof value === "bigint") {
|
|
10
|
+
return value.toString();
|
|
11
|
+
}
|
|
12
|
+
return value;
|
|
13
|
+
};
|
|
14
|
+
const bigIntDeserializer = (key, value) => {
|
|
15
|
+
if (typeof value === "string" && /^\d+$/.test(value) && value.length > 15) {
|
|
16
|
+
try {
|
|
17
|
+
return BigInt(value);
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
return value;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return value;
|
|
24
|
+
};
|
|
25
|
+
class RedisRepository {
|
|
26
|
+
constructor(fastify) {
|
|
27
|
+
this.redis = fastify.redis;
|
|
28
|
+
this.logger = fastify.log;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Set a string value with TTL (Time To Live)
|
|
32
|
+
* @param key Redis key
|
|
33
|
+
* @param value String value to store
|
|
34
|
+
* @param ttlSeconds TTL in seconds
|
|
35
|
+
*/
|
|
36
|
+
async setString(key, value, ttlSeconds) {
|
|
37
|
+
try {
|
|
38
|
+
if (ttlSeconds !== undefined) {
|
|
39
|
+
await this.redis.setex(key, ttlSeconds, value);
|
|
40
|
+
this.logger.debug(`Set key: ${key}, value: ${value}, ttl: ${ttlSeconds}s`);
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
await this.redis.set(key, value);
|
|
44
|
+
this.logger.debug(`Set key: ${key}, value: ${value}`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
this.logger.error(`Error setting string key: ${key}`, error);
|
|
49
|
+
throw error;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Get a string value by key
|
|
54
|
+
* @param key Redis key
|
|
55
|
+
* @returns Promise<string | null> The stored value or null if not found
|
|
56
|
+
*/
|
|
57
|
+
async getString(key) {
|
|
58
|
+
try {
|
|
59
|
+
const value = await this.redis.get(key);
|
|
60
|
+
this.logger.debug(`Get key: ${key}, value: ${value}`);
|
|
61
|
+
return value;
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
this.logger.error(`Error getting string key: ${key}`, error);
|
|
65
|
+
throw error;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Set an object as JSON string with optional TTL
|
|
70
|
+
* @param key Redis key
|
|
71
|
+
* @param object Object to store
|
|
72
|
+
* @param ttlSeconds Optional TTL in seconds
|
|
73
|
+
*/
|
|
74
|
+
async setObject(key, object, ttlSeconds) {
|
|
75
|
+
try {
|
|
76
|
+
const jsonValue = JSON.stringify(object, bigIntSerializer);
|
|
77
|
+
if (ttlSeconds !== undefined) {
|
|
78
|
+
await this.redis.setex(key, ttlSeconds, jsonValue);
|
|
79
|
+
this.logger.debug(`Set object key: ${key}, ttl: ${ttlSeconds}s`);
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
await this.redis.set(key, jsonValue);
|
|
83
|
+
this.logger.debug(`Set object key: ${key}`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
catch (error) {
|
|
87
|
+
this.logger.error(`Error setting object key: ${key}`, error);
|
|
88
|
+
throw error;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Get an object by key and parse as JSON
|
|
93
|
+
* @param key Redis key
|
|
94
|
+
* @returns Promise<T | null> The parsed object or null if not found
|
|
95
|
+
*/
|
|
96
|
+
async getObject(key) {
|
|
97
|
+
try {
|
|
98
|
+
const jsonValue = await this.redis.get(key);
|
|
99
|
+
if (jsonValue === null) {
|
|
100
|
+
this.logger.debug(`Get object key: ${key}, found: false`);
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
const object = JSON.parse(jsonValue, bigIntDeserializer);
|
|
104
|
+
this.logger.debug(`Get object key: ${key}, found: true`);
|
|
105
|
+
return object;
|
|
106
|
+
}
|
|
107
|
+
catch (error) {
|
|
108
|
+
this.logger.error(`Error getting object key: ${key}`, error);
|
|
109
|
+
throw error;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Get a Redis hash map operations
|
|
114
|
+
* @param mapName Hash map name
|
|
115
|
+
* @returns RedisHashMap instance for operations
|
|
116
|
+
*/
|
|
117
|
+
getMap(mapName) {
|
|
118
|
+
return new RedisHashMap(mapName, this.redis, this.logger);
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Delete a key from Redis
|
|
122
|
+
* @param key Redis key to delete
|
|
123
|
+
* @returns Promise<boolean> true if key was deleted, false if key didn't exist
|
|
124
|
+
*/
|
|
125
|
+
async delete(key) {
|
|
126
|
+
try {
|
|
127
|
+
const result = await this.redis.del(key);
|
|
128
|
+
const deleted = result > 0;
|
|
129
|
+
this.logger.debug(`Delete key: ${key}, success: ${deleted}`);
|
|
130
|
+
return deleted;
|
|
131
|
+
}
|
|
132
|
+
catch (error) {
|
|
133
|
+
this.logger.error(`Error deleting key: ${key}`, error);
|
|
134
|
+
throw error;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Check if a key exists in Redis
|
|
139
|
+
* @param key Redis key to check
|
|
140
|
+
* @returns Promise<boolean> true if key exists, false otherwise
|
|
141
|
+
*/
|
|
142
|
+
async exists(key) {
|
|
143
|
+
try {
|
|
144
|
+
const result = await this.redis.exists(key);
|
|
145
|
+
const exists = result === 1;
|
|
146
|
+
this.logger.debug(`Key exists: ${key}, result: ${exists}`);
|
|
147
|
+
return exists;
|
|
148
|
+
}
|
|
149
|
+
catch (error) {
|
|
150
|
+
this.logger.error(`Error checking key existence: ${key}`, error);
|
|
151
|
+
throw error;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Set expiry time for an existing key
|
|
156
|
+
* @param key Redis key
|
|
157
|
+
* @param ttlSeconds TTL in seconds
|
|
158
|
+
* @returns Promise<boolean> true if expiry was set, false if key doesn't exist
|
|
159
|
+
*/
|
|
160
|
+
async expire(key, ttlSeconds) {
|
|
161
|
+
try {
|
|
162
|
+
const result = await this.redis.expire(key, ttlSeconds);
|
|
163
|
+
this.logger.debug(`Set expiry for key: ${key}, ttl: ${ttlSeconds}s, success: ${result === 1}`);
|
|
164
|
+
return result === 1;
|
|
165
|
+
}
|
|
166
|
+
catch (error) {
|
|
167
|
+
this.logger.error(`Error setting expiry for key: ${key}`, error);
|
|
168
|
+
throw error;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Get TTL (time to live) for a key
|
|
173
|
+
* @param key Redis key
|
|
174
|
+
* @returns Promise<number> TTL in seconds, -1 if no expiry, -2 if key doesn't exist
|
|
175
|
+
*/
|
|
176
|
+
async getTTL(key) {
|
|
177
|
+
try {
|
|
178
|
+
const ttl = await this.redis.ttl(key);
|
|
179
|
+
this.logger.debug(`Get TTL for key: ${key}, ttl: ${ttl}s`);
|
|
180
|
+
return ttl;
|
|
181
|
+
}
|
|
182
|
+
catch (error) {
|
|
183
|
+
this.logger.error(`Error getting TTL for key: ${key}`, error);
|
|
184
|
+
throw error;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Increment a numeric value stored at key
|
|
189
|
+
* @param key Redis key
|
|
190
|
+
* @param increment Amount to increment by (default: 1)
|
|
191
|
+
* @returns Promise<number> The new value after increment
|
|
192
|
+
*/
|
|
193
|
+
async increment(key, increment = 1) {
|
|
194
|
+
try {
|
|
195
|
+
const result = await this.redis.incrby(key, increment);
|
|
196
|
+
this.logger.debug(`Increment key: ${key}, by: ${increment}, new value: ${result}`);
|
|
197
|
+
return result;
|
|
198
|
+
}
|
|
199
|
+
catch (error) {
|
|
200
|
+
this.logger.error(`Error incrementing key: ${key}`, error);
|
|
201
|
+
throw error;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Get multiple keys at once
|
|
206
|
+
* @param keys Array of Redis keys
|
|
207
|
+
* @returns Promise<(string | null)[]> Array of values corresponding to keys
|
|
208
|
+
*/
|
|
209
|
+
async getMultiple(keys) {
|
|
210
|
+
try {
|
|
211
|
+
if (keys.length === 0)
|
|
212
|
+
return [];
|
|
213
|
+
const values = await this.redis.mget(...keys);
|
|
214
|
+
this.logger.debug(`Get multiple keys: ${keys.length} keys`);
|
|
215
|
+
return values;
|
|
216
|
+
}
|
|
217
|
+
catch (error) {
|
|
218
|
+
this.logger.error(`Error getting multiple keys`, error);
|
|
219
|
+
throw error;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Set multiple key-value pairs at once
|
|
224
|
+
* @param keyValues Object containing key-value pairs
|
|
225
|
+
* @returns Promise<void>
|
|
226
|
+
*/
|
|
227
|
+
async setMultiple(keyValues) {
|
|
228
|
+
try {
|
|
229
|
+
const pairs = [];
|
|
230
|
+
Object.entries(keyValues).forEach(([key, value]) => {
|
|
231
|
+
pairs.push(key, value);
|
|
232
|
+
});
|
|
233
|
+
if (pairs.length > 0) {
|
|
234
|
+
await this.redis.mset(...pairs);
|
|
235
|
+
this.logger.debug(`Set multiple keys: ${Object.keys(keyValues).length} keys`);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
catch (error) {
|
|
239
|
+
this.logger.error(`Error setting multiple keys`, error);
|
|
240
|
+
throw error;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Find keys matching a pattern using KEYS command
|
|
245
|
+
* WARNING: This command can be slow on large datasets. Consider using SCAN for production.
|
|
246
|
+
* @param pattern Pattern to match keys (supports wildcards: *, ?, [])
|
|
247
|
+
* @returns Promise<string[]> Array of matching key names
|
|
248
|
+
*
|
|
249
|
+
* Pattern examples:
|
|
250
|
+
* - "user:*" - matches all keys starting with "user:"
|
|
251
|
+
* - "session:*:data" - matches keys like "session:123:data"
|
|
252
|
+
* - "cache:user:[0-9]*" - matches keys like "cache:user:123"
|
|
253
|
+
*/
|
|
254
|
+
async keys(pattern) {
|
|
255
|
+
try {
|
|
256
|
+
const keys = await this.redis.keys(pattern);
|
|
257
|
+
this.logger.debug(`Keys pattern: ${pattern}, found: ${keys.length} keys`);
|
|
258
|
+
return keys;
|
|
259
|
+
}
|
|
260
|
+
catch (error) {
|
|
261
|
+
this.logger.error(`Error finding keys with pattern: ${pattern}`, error);
|
|
262
|
+
throw error;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Scan keys matching a pattern using SCAN command (recommended for production)
|
|
267
|
+
* This is more efficient than KEYS for large datasets as it doesn't block Redis
|
|
268
|
+
* @param pattern Pattern to match keys
|
|
269
|
+
* @param count Number of keys to return per iteration (default: 10)
|
|
270
|
+
* @returns Promise<string[]> Array of matching key names
|
|
271
|
+
*/
|
|
272
|
+
async scanKeys(pattern, count = 10) {
|
|
273
|
+
try {
|
|
274
|
+
const keys = [];
|
|
275
|
+
let cursor = "0";
|
|
276
|
+
do {
|
|
277
|
+
const result = await this.redis.scan(cursor, "MATCH", pattern, "COUNT", count);
|
|
278
|
+
cursor = result[0];
|
|
279
|
+
keys.push(...result[1]);
|
|
280
|
+
} while (cursor !== "0");
|
|
281
|
+
this.logger.debug(`Scan keys pattern: ${pattern}, found: ${keys.length} keys`);
|
|
282
|
+
return keys;
|
|
283
|
+
}
|
|
284
|
+
catch (error) {
|
|
285
|
+
this.logger.error(`Error scanning keys with pattern: ${pattern}`, error);
|
|
286
|
+
throw error;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
// ==================== REDIS SET OPERATIONS ====================
|
|
290
|
+
/**
|
|
291
|
+
* Add one or more members to a Redis set
|
|
292
|
+
* @param key Redis set key
|
|
293
|
+
* @param members Members to add to the set
|
|
294
|
+
* @returns Promise<number> Number of members that were added (excluding already existing members)
|
|
295
|
+
*/
|
|
296
|
+
async setAdd(key, ...members) {
|
|
297
|
+
try {
|
|
298
|
+
const result = await this.redis.sadd(key, ...members);
|
|
299
|
+
this.logger.debug(`Set add - key: ${key}, members: ${members.length}, added: ${result}`);
|
|
300
|
+
return result;
|
|
301
|
+
}
|
|
302
|
+
catch (error) {
|
|
303
|
+
this.logger.error(`Error adding to set: ${key}`, error);
|
|
304
|
+
throw error;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Remove one or more members from a Redis set
|
|
309
|
+
* @param key Redis set key
|
|
310
|
+
* @param members Members to remove from the set
|
|
311
|
+
* @returns Promise<number> Number of members that were removed
|
|
312
|
+
*/
|
|
313
|
+
async setRemove(key, ...members) {
|
|
314
|
+
try {
|
|
315
|
+
const result = await this.redis.srem(key, ...members);
|
|
316
|
+
this.logger.debug(`Set remove - key: ${key}, members: ${members.length}, removed: ${result}`);
|
|
317
|
+
return result;
|
|
318
|
+
}
|
|
319
|
+
catch (error) {
|
|
320
|
+
this.logger.error(`Error removing from set: ${key}`, error);
|
|
321
|
+
throw error;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
/**
|
|
325
|
+
* Get the number of members in a Redis set
|
|
326
|
+
* @param key Redis set key
|
|
327
|
+
* @returns Promise<number> Number of members in the set
|
|
328
|
+
*/
|
|
329
|
+
async setSize(key) {
|
|
330
|
+
try {
|
|
331
|
+
const size = await this.redis.scard(key);
|
|
332
|
+
this.logger.debug(`Set size - key: ${key}, size: ${size}`);
|
|
333
|
+
return size;
|
|
334
|
+
}
|
|
335
|
+
catch (error) {
|
|
336
|
+
this.logger.error(`Error getting set size: ${key}`, error);
|
|
337
|
+
throw error;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
/**
|
|
341
|
+
* Check if a member exists in a Redis set
|
|
342
|
+
* @param key Redis set key
|
|
343
|
+
* @param member Member to check
|
|
344
|
+
* @returns Promise<boolean> true if member exists in the set
|
|
345
|
+
*/
|
|
346
|
+
async setContains(key, member) {
|
|
347
|
+
try {
|
|
348
|
+
const result = await this.redis.sismember(key, member);
|
|
349
|
+
const exists = result === 1;
|
|
350
|
+
this.logger.debug(`Set contains - key: ${key}, member: ${member}, exists: ${exists}`);
|
|
351
|
+
return exists;
|
|
352
|
+
}
|
|
353
|
+
catch (error) {
|
|
354
|
+
this.logger.error(`Error checking set membership: ${key}`, error);
|
|
355
|
+
throw error;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
/**
|
|
359
|
+
* Get all members of a Redis set
|
|
360
|
+
* @param key Redis set key
|
|
361
|
+
* @returns Promise<string[]> Array of all members in the set
|
|
362
|
+
*/
|
|
363
|
+
async setMembers(key) {
|
|
364
|
+
try {
|
|
365
|
+
const members = await this.redis.smembers(key);
|
|
366
|
+
this.logger.debug(`Set members - key: ${key}, count: ${members.length}`);
|
|
367
|
+
return members;
|
|
368
|
+
}
|
|
369
|
+
catch (error) {
|
|
370
|
+
this.logger.error(`Error getting set members: ${key}`, error);
|
|
371
|
+
throw error;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
/**
|
|
375
|
+
* Get a random member from a Redis set
|
|
376
|
+
* @param key Redis set key
|
|
377
|
+
* @param count Number of random members to return (default: 1)
|
|
378
|
+
* @returns Promise<string[]> Array of random members
|
|
379
|
+
*/
|
|
380
|
+
async setRandomMembers(key, count = 1) {
|
|
381
|
+
try {
|
|
382
|
+
const members = await this.redis.srandmember(key, count);
|
|
383
|
+
const result = Array.isArray(members)
|
|
384
|
+
? members
|
|
385
|
+
: [members].filter(Boolean);
|
|
386
|
+
this.logger.debug(`Set random members - key: ${key}, count: ${count}, returned: ${result.length}`);
|
|
387
|
+
return result;
|
|
388
|
+
}
|
|
389
|
+
catch (error) {
|
|
390
|
+
this.logger.error(`Error getting random set members: ${key}`, error);
|
|
391
|
+
throw error;
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
/**
|
|
395
|
+
* Pop (remove and return) a random member from a Redis set
|
|
396
|
+
* @param key Redis set key
|
|
397
|
+
* @param count Number of members to pop (default: 1)
|
|
398
|
+
* @returns Promise<string[]> Array of popped members
|
|
399
|
+
*/
|
|
400
|
+
async setPop(key, count = 1) {
|
|
401
|
+
try {
|
|
402
|
+
const members = await this.redis.spop(key, count);
|
|
403
|
+
const result = Array.isArray(members)
|
|
404
|
+
? members
|
|
405
|
+
: [members].filter(Boolean);
|
|
406
|
+
this.logger.debug(`Set pop - key: ${key}, count: ${count}, popped: ${result.length}`);
|
|
407
|
+
return result;
|
|
408
|
+
}
|
|
409
|
+
catch (error) {
|
|
410
|
+
this.logger.error(`Error popping from set: ${key}`, error);
|
|
411
|
+
throw error;
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
async hSet(key, field, value) {
|
|
415
|
+
try {
|
|
416
|
+
const jsonValue = typeof value === "string"
|
|
417
|
+
? value
|
|
418
|
+
: JSON.stringify(value, bigIntSerializer);
|
|
419
|
+
await this.redis.hset(key, field, jsonValue);
|
|
420
|
+
this.logger.debug(`Hset - key: ${key}`);
|
|
421
|
+
}
|
|
422
|
+
catch (error) {
|
|
423
|
+
this.logger.error(`Error hset: ${key}`, error);
|
|
424
|
+
throw error;
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
async hSetMultiple(key, values) {
|
|
428
|
+
const serializedValues = Object.entries(values).reduce((acc, [key, value]) => {
|
|
429
|
+
acc[key] = JSON.stringify(value, bigIntSerializer);
|
|
430
|
+
return acc;
|
|
431
|
+
}, {});
|
|
432
|
+
await this.redis.hset(key, serializedValues);
|
|
433
|
+
}
|
|
434
|
+
async hGet(key, field) {
|
|
435
|
+
try {
|
|
436
|
+
const value = await this.redis.hget(key, field);
|
|
437
|
+
if (value === null) {
|
|
438
|
+
return null;
|
|
439
|
+
}
|
|
440
|
+
return JSON.parse(value, bigIntDeserializer);
|
|
441
|
+
}
|
|
442
|
+
catch (error) {
|
|
443
|
+
this.logger.error(`Error hgetall: ${key}`, error);
|
|
444
|
+
throw error;
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
async hGetAll(key) {
|
|
448
|
+
try {
|
|
449
|
+
const value = await this.redis.hgetall(key);
|
|
450
|
+
Object.keys(value).forEach((key) => {
|
|
451
|
+
value[key] = JSON.parse(value[key]);
|
|
452
|
+
});
|
|
453
|
+
return value;
|
|
454
|
+
}
|
|
455
|
+
catch (error) {
|
|
456
|
+
throw error;
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
async hDel(key) {
|
|
460
|
+
try {
|
|
461
|
+
await this.redis.hdel(key);
|
|
462
|
+
this.logger.debug(`Hdel - key: ${key}`);
|
|
463
|
+
}
|
|
464
|
+
catch (error) {
|
|
465
|
+
this.logger.error(`Error hdel: ${key}`, error);
|
|
466
|
+
throw error;
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
async hDelItem(key, field) {
|
|
470
|
+
try {
|
|
471
|
+
await this.redis.hdel(key, field);
|
|
472
|
+
this.logger.debug(`Hdel - key: ${key}, field: ${field}`);
|
|
473
|
+
}
|
|
474
|
+
catch (error) {
|
|
475
|
+
this.logger.error(`Error hdel: ${key}`, error);
|
|
476
|
+
throw error;
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
exports.RedisRepository = RedisRepository;
|
|
481
|
+
/**
|
|
482
|
+
* Helper class for Redis Hash Map operations
|
|
483
|
+
*/
|
|
484
|
+
class RedisHashMap {
|
|
485
|
+
constructor(mapName, redis, logger) {
|
|
486
|
+
this.mapName = mapName;
|
|
487
|
+
this.redis = redis;
|
|
488
|
+
this.logger = logger;
|
|
489
|
+
}
|
|
490
|
+
/**
|
|
491
|
+
* Set a field in the hash map
|
|
492
|
+
* @param field Hash field name
|
|
493
|
+
* @param value Value to set
|
|
494
|
+
*/
|
|
495
|
+
async set(field, value) {
|
|
496
|
+
try {
|
|
497
|
+
const jsonValue = typeof value === "string"
|
|
498
|
+
? value
|
|
499
|
+
: JSON.stringify(value, bigIntSerializer);
|
|
500
|
+
await this.redis.hset(this.mapName, field, jsonValue);
|
|
501
|
+
this.logger.debug(`Hash set - map: ${this.mapName}, field: ${field}`);
|
|
502
|
+
}
|
|
503
|
+
catch (error) {
|
|
504
|
+
this.logger.error(`Error setting hash field: ${this.mapName}.${field}`, error);
|
|
505
|
+
throw error;
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
/**
|
|
509
|
+
* Get a field from the hash map
|
|
510
|
+
* @param field Hash field name
|
|
511
|
+
* @returns Promise<T | null> The field value or null if not found
|
|
512
|
+
*/
|
|
513
|
+
async get(field) {
|
|
514
|
+
try {
|
|
515
|
+
const value = await this.redis.hget(this.mapName, field);
|
|
516
|
+
if (value === null) {
|
|
517
|
+
this.logger.debug(`Hash get - map: ${this.mapName}, field: ${field}, found: false`);
|
|
518
|
+
return null;
|
|
519
|
+
}
|
|
520
|
+
try {
|
|
521
|
+
const parsed = JSON.parse(value, bigIntDeserializer);
|
|
522
|
+
this.logger.debug(`Hash get - map: ${this.mapName}, field: ${field}, found: true`);
|
|
523
|
+
return parsed;
|
|
524
|
+
}
|
|
525
|
+
catch {
|
|
526
|
+
// If JSON parsing fails, return as string
|
|
527
|
+
this.logger.debug(`Hash get - map: ${this.mapName}, field: ${field}, found: true (string)`);
|
|
528
|
+
return value;
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
catch (error) {
|
|
532
|
+
this.logger.error(`Error getting hash field: ${this.mapName}.${field}`, error);
|
|
533
|
+
throw error;
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
/**
|
|
537
|
+
* Get all fields and values from the hash map
|
|
538
|
+
* @returns Promise<Record<string, string>> All field-value pairs
|
|
539
|
+
*/
|
|
540
|
+
async getAll() {
|
|
541
|
+
try {
|
|
542
|
+
const result = await this.redis.hgetall(this.mapName);
|
|
543
|
+
this.logger.debug(`Hash get all - map: ${this.mapName}, fields: ${Object.keys(result).length}`);
|
|
544
|
+
return result;
|
|
545
|
+
}
|
|
546
|
+
catch (error) {
|
|
547
|
+
this.logger.error(`Error getting all hash fields: ${this.mapName}`, error);
|
|
548
|
+
throw error;
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
/**
|
|
552
|
+
* Delete a field from the hash map
|
|
553
|
+
* @param field Hash field name
|
|
554
|
+
* @returns Promise<boolean> true if field was deleted
|
|
555
|
+
*/
|
|
556
|
+
async delete(field) {
|
|
557
|
+
try {
|
|
558
|
+
const result = await this.redis.hdel(this.mapName, field);
|
|
559
|
+
const deleted = result > 0;
|
|
560
|
+
this.logger.debug(`Hash delete - map: ${this.mapName}, field: ${field}, success: ${deleted}`);
|
|
561
|
+
return deleted;
|
|
562
|
+
}
|
|
563
|
+
catch (error) {
|
|
564
|
+
this.logger.error(`Error deleting hash field: ${this.mapName}.${field}`, error);
|
|
565
|
+
throw error;
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
/**
|
|
569
|
+
* Check if a field exists in the hash map
|
|
570
|
+
* @param field Hash field name
|
|
571
|
+
* @returns Promise<boolean> true if field exists
|
|
572
|
+
*/
|
|
573
|
+
async exists(field) {
|
|
574
|
+
try {
|
|
575
|
+
const result = await this.redis.hexists(this.mapName, field);
|
|
576
|
+
this.logger.debug(`Hash exists - map: ${this.mapName}, field: ${field}, exists: ${result === 1}`);
|
|
577
|
+
return result === 1;
|
|
578
|
+
}
|
|
579
|
+
catch (error) {
|
|
580
|
+
this.logger.error(`Error checking hash field existence: ${this.mapName}.${field}`, error);
|
|
581
|
+
throw error;
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
/**
|
|
585
|
+
* Get all field names in the hash map
|
|
586
|
+
* @returns Promise<string[]> Array of field names
|
|
587
|
+
*/
|
|
588
|
+
async getKeys() {
|
|
589
|
+
try {
|
|
590
|
+
const keys = await this.redis.hkeys(this.mapName);
|
|
591
|
+
this.logger.debug(`Hash keys - map: ${this.mapName}, count: ${keys.length}`);
|
|
592
|
+
return keys;
|
|
593
|
+
}
|
|
594
|
+
catch (error) {
|
|
595
|
+
this.logger.error(`Error getting hash keys: ${this.mapName}`, error);
|
|
596
|
+
throw error;
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
/**
|
|
600
|
+
* Get the number of fields in the hash map
|
|
601
|
+
* @returns Promise<number> Number of fields
|
|
602
|
+
*/
|
|
603
|
+
async size() {
|
|
604
|
+
try {
|
|
605
|
+
const size = await this.redis.hlen(this.mapName);
|
|
606
|
+
this.logger.debug(`Hash size - map: ${this.mapName}, size: ${size}`);
|
|
607
|
+
return size;
|
|
608
|
+
}
|
|
609
|
+
catch (error) {
|
|
610
|
+
this.logger.error(`Error getting hash size: ${this.mapName}`, error);
|
|
611
|
+
throw error;
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
exports.RedisHashMap = RedisHashMap;
|
|
616
|
+
exports.default = (0, fastify_plugin_1.default)(async (fastify) => {
|
|
617
|
+
const repo = new RedisRepository(fastify);
|
|
618
|
+
fastify.decorate("redisRepository", repo);
|
|
619
|
+
}, { name: "redis-repository", dependencies: ["redis"] });
|
package/package.json
CHANGED
|
@@ -1,38 +1,41 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@ecashlib/shared-message",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"description": "Shared utilities for Ecash microservices - Message handling, i18n, error helpers",
|
|
5
|
-
"main": "dist/index.js",
|
|
6
|
-
"types": "dist/index.d.ts",
|
|
7
|
-
"scripts": {
|
|
8
|
-
"build": "tsc",
|
|
9
|
-
"watch": "tsc --watch",
|
|
10
|
-
"prepublishOnly": "npm run build",
|
|
11
|
-
"clean": "rimraf dist"
|
|
12
|
-
},
|
|
13
|
-
"keywords": [
|
|
14
|
-
"ecash",
|
|
15
|
-
"ecashlib",
|
|
16
|
-
"shared",
|
|
17
|
-
"i18n",
|
|
18
|
-
"message",
|
|
19
|
-
"error-handling"
|
|
20
|
-
],
|
|
21
|
-
"author": "Ecash Team",
|
|
22
|
-
"license": "PRIVATE",
|
|
23
|
-
"publishConfig": {
|
|
24
|
-
"access": "public"
|
|
25
|
-
},
|
|
26
|
-
"dependencies": {
|
|
27
|
-
"fastify": "^
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
"
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
"
|
|
36
|
-
|
|
37
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "@ecashlib/shared-message",
|
|
3
|
+
"version": "1.0.3",
|
|
4
|
+
"description": "Shared utilities for Ecash microservices - Message handling, i18n, error helpers",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build": "tsc",
|
|
9
|
+
"watch": "tsc --watch",
|
|
10
|
+
"prepublishOnly": "npm run build",
|
|
11
|
+
"clean": "rimraf dist"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [
|
|
14
|
+
"ecash",
|
|
15
|
+
"ecashlib",
|
|
16
|
+
"shared",
|
|
17
|
+
"i18n",
|
|
18
|
+
"message",
|
|
19
|
+
"error-handling"
|
|
20
|
+
],
|
|
21
|
+
"author": "Ecash Team",
|
|
22
|
+
"license": "PRIVATE",
|
|
23
|
+
"publishConfig": {
|
|
24
|
+
"access": "public"
|
|
25
|
+
},
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"@fastify/redis": "^7.0.0",
|
|
28
|
+
"fastify": "^4.0.0",
|
|
29
|
+
"fastify-plugin": "^4.0.0",
|
|
30
|
+
"ioredis": "^5.0.0"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@types/node": "^20.0.0",
|
|
34
|
+
"typescript": "^5.0.0",
|
|
35
|
+
"rimraf": "^5.0.0"
|
|
36
|
+
},
|
|
37
|
+
"files": [
|
|
38
|
+
"dist",
|
|
39
|
+
"README.md"
|
|
40
|
+
]
|
|
38
41
|
}
|