@uber-clone/common 1.0.6 → 1.0.8
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/build/index.d.ts +2 -0
- package/build/index.js +2 -2
- package/build/redis/redis-client.d.ts +51 -0
- package/build/redis/redis-client.js +309 -0
- package/package.json +4 -2
package/build/index.d.ts
CHANGED
package/build/index.js
CHANGED
|
@@ -24,9 +24,9 @@ __exportStar(require("./middlewares/current-user"), exports);
|
|
|
24
24
|
__exportStar(require("./middlewares/error-handler"), exports);
|
|
25
25
|
__exportStar(require("./middlewares/require-auth"), exports);
|
|
26
26
|
__exportStar(require("./middlewares/validate-request"), exports);
|
|
27
|
-
// export * from "./events/kafka-listener";
|
|
28
|
-
// export * from "./events/kafka-publisher";
|
|
29
27
|
__exportStar(require("./events/kafka-client"), exports);
|
|
30
28
|
__exportStar(require("./events/types"), exports);
|
|
31
29
|
__exportStar(require("./events/subjects"), exports);
|
|
32
30
|
__exportStar(require("./metrics/app.metrics"), exports);
|
|
31
|
+
__exportStar(require("./utils/logger"), exports);
|
|
32
|
+
__exportStar(require("./redis/redis-client"), exports);
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { RedisClientType } from "redis";
|
|
2
|
+
import Redlock from "redlock";
|
|
3
|
+
import { Logger } from "../utils/logger";
|
|
4
|
+
export interface GeoLocation {
|
|
5
|
+
type?: "Point";
|
|
6
|
+
coordinates: [number, number];
|
|
7
|
+
address?: string;
|
|
8
|
+
city?: string;
|
|
9
|
+
state?: string;
|
|
10
|
+
country?: string;
|
|
11
|
+
postalCode?: string;
|
|
12
|
+
}
|
|
13
|
+
export interface RedisConfig {
|
|
14
|
+
url?: string;
|
|
15
|
+
host?: string;
|
|
16
|
+
port?: number;
|
|
17
|
+
password?: string;
|
|
18
|
+
db?: number;
|
|
19
|
+
cluster?: boolean;
|
|
20
|
+
connectTimeout?: number;
|
|
21
|
+
lazyConnect?: boolean;
|
|
22
|
+
retryDelay?: number;
|
|
23
|
+
maxRetries?: number;
|
|
24
|
+
}
|
|
25
|
+
export declare class RedisClient {
|
|
26
|
+
private config;
|
|
27
|
+
protected static instance: RedisClient;
|
|
28
|
+
protected redis: RedisClientType;
|
|
29
|
+
protected redlock: Redlock | null;
|
|
30
|
+
protected logger: Logger;
|
|
31
|
+
protected isConnected: boolean;
|
|
32
|
+
constructor(config?: RedisConfig);
|
|
33
|
+
static getInstance(): RedisClient;
|
|
34
|
+
private initializeRedlock;
|
|
35
|
+
private setupEventListeners;
|
|
36
|
+
connect(): Promise<void>;
|
|
37
|
+
disconnect(): Promise<void>;
|
|
38
|
+
private ensureConnected;
|
|
39
|
+
healthCheck(): Promise<boolean>;
|
|
40
|
+
incrementCounter(key: string, incrementBy?: number): Promise<number>;
|
|
41
|
+
getCounterValue(key: string): Promise<number>;
|
|
42
|
+
acquireLock(lockKey: string, ttl?: number): Promise<any | string | null>;
|
|
43
|
+
releaseLock(lockOrKey: any | string | null): Promise<boolean>;
|
|
44
|
+
setCache(key: string, value: any, ttl?: number): Promise<void>;
|
|
45
|
+
getCache<T>(key: string): Promise<T | null>;
|
|
46
|
+
deleteCache(key: string): Promise<void>;
|
|
47
|
+
publish(channel: string, message: any): Promise<void>;
|
|
48
|
+
subscribe(channel: string, handler: (message: any) => void): Promise<void>;
|
|
49
|
+
flushAll(): Promise<void>;
|
|
50
|
+
getRedisInfo(): Promise<Record<string, string>>;
|
|
51
|
+
}
|
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.RedisClient = void 0;
|
|
16
|
+
// libs/common/src/redis/redis-client.ts
|
|
17
|
+
const redis_1 = require("redis");
|
|
18
|
+
const redlock_1 = __importDefault(require("redlock"));
|
|
19
|
+
const logger_1 = require("../utils/logger");
|
|
20
|
+
class RedisClient {
|
|
21
|
+
constructor(config = {}) {
|
|
22
|
+
this.config = config;
|
|
23
|
+
this.redlock = null;
|
|
24
|
+
this.logger = new logger_1.Logger("RedisClient");
|
|
25
|
+
this.isConnected = false;
|
|
26
|
+
this.redis = (0, redis_1.createClient)({
|
|
27
|
+
url: process.env.REDIS_URL || "redis://redis-cluster:6379",
|
|
28
|
+
socket: {
|
|
29
|
+
reconnectStrategy: (retries) => {
|
|
30
|
+
this.logger.debug(`Redis reconnection attempt ${retries}`);
|
|
31
|
+
return Math.min(retries * 1000, 10000);
|
|
32
|
+
},
|
|
33
|
+
connectTimeout: 10000,
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
this.initializeRedlock();
|
|
37
|
+
this.setupEventListeners();
|
|
38
|
+
this.connect().catch((err) => this.logger.error("Auto-connect failed:", err));
|
|
39
|
+
}
|
|
40
|
+
static getInstance() {
|
|
41
|
+
if (!this.instance) {
|
|
42
|
+
this.instance = new RedisClient();
|
|
43
|
+
}
|
|
44
|
+
return this.instance;
|
|
45
|
+
}
|
|
46
|
+
initializeRedlock() {
|
|
47
|
+
try {
|
|
48
|
+
this.redlock = new redlock_1.default([this.redis], {
|
|
49
|
+
driftFactor: 0.01,
|
|
50
|
+
retryCount: 3,
|
|
51
|
+
retryDelay: 200,
|
|
52
|
+
retryJitter: 100,
|
|
53
|
+
});
|
|
54
|
+
this.redlock.on("error", (error) => {
|
|
55
|
+
this.logger.error("Redlock error:", error);
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
catch (err) {
|
|
59
|
+
this.logger.warn("Redlock initialization failed:", err);
|
|
60
|
+
this.redlock = null;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
setupEventListeners() {
|
|
64
|
+
this.redis.on("connect", () => {
|
|
65
|
+
this.isConnected = true;
|
|
66
|
+
this.logger.info("✅ Connected to Redis");
|
|
67
|
+
});
|
|
68
|
+
this.redis.on("error", (error) => {
|
|
69
|
+
this.logger.error("❌ Redis error:", error);
|
|
70
|
+
this.isConnected = false;
|
|
71
|
+
});
|
|
72
|
+
this.redis.on("end", () => {
|
|
73
|
+
this.logger.warn("⚠️ Redis connection closed");
|
|
74
|
+
this.isConnected = false;
|
|
75
|
+
});
|
|
76
|
+
this.redis.on("ready", () => {
|
|
77
|
+
this.logger.info("Redis client ready");
|
|
78
|
+
this.isConnected = true;
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
// Core Connection Methods
|
|
82
|
+
connect() {
|
|
83
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
84
|
+
if (this.isConnected)
|
|
85
|
+
return;
|
|
86
|
+
try {
|
|
87
|
+
yield this.redis.connect();
|
|
88
|
+
this.isConnected = true;
|
|
89
|
+
}
|
|
90
|
+
catch (error) {
|
|
91
|
+
this.logger.error("Failed to connect to Redis:", error);
|
|
92
|
+
throw error;
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
disconnect() {
|
|
97
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
98
|
+
try {
|
|
99
|
+
yield this.redis.quit();
|
|
100
|
+
this.isConnected = false;
|
|
101
|
+
this.logger.info("Redis connection closed");
|
|
102
|
+
}
|
|
103
|
+
catch (error) {
|
|
104
|
+
this.logger.error("Error disconnecting from Redis:", error);
|
|
105
|
+
throw error;
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
ensureConnected() {
|
|
110
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
111
|
+
if (!this.isConnected) {
|
|
112
|
+
yield this.connect();
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
// Health Check
|
|
117
|
+
healthCheck() {
|
|
118
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
119
|
+
try {
|
|
120
|
+
yield this.redis.ping();
|
|
121
|
+
return true;
|
|
122
|
+
}
|
|
123
|
+
catch (error) {
|
|
124
|
+
this.logger.error("Redis health check failed:", error);
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
incrementCounter(key_1) {
|
|
130
|
+
return __awaiter(this, arguments, void 0, function* (key, incrementBy = 1) {
|
|
131
|
+
return yield this.redis.incrBy(key, incrementBy);
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
getCounterValue(key) {
|
|
135
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
136
|
+
const val = yield this.redis.get(key);
|
|
137
|
+
return val ? parseInt(val) : 0;
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
// ========== LOCKING METHODS ==========
|
|
141
|
+
acquireLock(lockKey_1) {
|
|
142
|
+
return __awaiter(this, arguments, void 0, function* (lockKey, ttl = 5000) {
|
|
143
|
+
yield this.ensureConnected();
|
|
144
|
+
if (this.redlock) {
|
|
145
|
+
try {
|
|
146
|
+
const lock = yield this.redlock.acquire([lockKey], ttl);
|
|
147
|
+
return lock;
|
|
148
|
+
}
|
|
149
|
+
catch (error) {
|
|
150
|
+
return null;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
// Fallback: SET NX
|
|
154
|
+
try {
|
|
155
|
+
const result = yield this.redis.set(lockKey, "locked", {
|
|
156
|
+
NX: true,
|
|
157
|
+
PX: ttl,
|
|
158
|
+
});
|
|
159
|
+
return result === "OK" ? lockKey : null;
|
|
160
|
+
}
|
|
161
|
+
catch (error) {
|
|
162
|
+
this.logger.error(`Error acquiring lock ${lockKey}:`, error);
|
|
163
|
+
return null;
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
releaseLock(lockOrKey) {
|
|
168
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
169
|
+
var _a;
|
|
170
|
+
if (!lockOrKey)
|
|
171
|
+
return true;
|
|
172
|
+
// Redlock instance
|
|
173
|
+
if (typeof lockOrKey === "object" &&
|
|
174
|
+
typeof lockOrKey.release === "function") {
|
|
175
|
+
try {
|
|
176
|
+
(_a = this.redlock) === null || _a === void 0 ? void 0 : _a.release(lockOrKey.resources[0]);
|
|
177
|
+
return true;
|
|
178
|
+
}
|
|
179
|
+
catch (error) {
|
|
180
|
+
this.logger.warn("Error releasing Redlock:", error);
|
|
181
|
+
return false;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
// Fallback: key string
|
|
185
|
+
if (typeof lockOrKey === "string") {
|
|
186
|
+
try {
|
|
187
|
+
yield this.redis.del(lockOrKey);
|
|
188
|
+
return true;
|
|
189
|
+
}
|
|
190
|
+
catch (error) {
|
|
191
|
+
this.logger.warn(`Error releasing lock ${lockOrKey}:`, error);
|
|
192
|
+
return false;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
return false;
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
// ========== CACHING METHODS ==========
|
|
199
|
+
setCache(key, value, ttl) {
|
|
200
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
201
|
+
try {
|
|
202
|
+
const serialized = typeof value === "string" ? value : JSON.stringify(value);
|
|
203
|
+
if (ttl) {
|
|
204
|
+
yield this.redis.setEx(key, ttl, serialized);
|
|
205
|
+
}
|
|
206
|
+
else {
|
|
207
|
+
yield this.redis.set(key, serialized);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
catch (error) {
|
|
211
|
+
this.logger.error(`Error setting cache key ${key}:`, error);
|
|
212
|
+
throw error;
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
getCache(key) {
|
|
217
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
218
|
+
try {
|
|
219
|
+
const value = yield this.redis.get(key);
|
|
220
|
+
if (!value)
|
|
221
|
+
return null;
|
|
222
|
+
try {
|
|
223
|
+
return JSON.parse(value);
|
|
224
|
+
}
|
|
225
|
+
catch (_a) {
|
|
226
|
+
return value;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
catch (error) {
|
|
230
|
+
this.logger.error(`Error getting cache key ${key}:`, error);
|
|
231
|
+
return null;
|
|
232
|
+
}
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
deleteCache(key) {
|
|
236
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
237
|
+
try {
|
|
238
|
+
yield this.redis.del(key);
|
|
239
|
+
}
|
|
240
|
+
catch (error) {
|
|
241
|
+
this.logger.error(`Error deleting cache key ${key}:`, error);
|
|
242
|
+
throw error;
|
|
243
|
+
}
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
// ========== PUB/SUB METHODS ==========
|
|
247
|
+
publish(channel, message) {
|
|
248
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
249
|
+
try {
|
|
250
|
+
yield this.redis.publish(channel, JSON.stringify(message));
|
|
251
|
+
}
|
|
252
|
+
catch (error) {
|
|
253
|
+
this.logger.error(`Error publishing to channel ${channel}:`, error);
|
|
254
|
+
throw error;
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
subscribe(channel, handler) {
|
|
259
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
260
|
+
const subscriber = this.redis.duplicate();
|
|
261
|
+
yield subscriber.connect();
|
|
262
|
+
yield subscriber.subscribe(channel, (message) => {
|
|
263
|
+
try {
|
|
264
|
+
handler(JSON.parse(message));
|
|
265
|
+
}
|
|
266
|
+
catch (error) {
|
|
267
|
+
this.logger.error(`Error handling message on ${channel}:`, error);
|
|
268
|
+
}
|
|
269
|
+
});
|
|
270
|
+
subscriber.on("error", (error) => {
|
|
271
|
+
this.logger.error(`Subscriber error for ${channel}:`, error);
|
|
272
|
+
});
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
// ========== UTILITY METHODS ==========
|
|
276
|
+
flushAll() {
|
|
277
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
278
|
+
try {
|
|
279
|
+
yield this.redis.flushAll();
|
|
280
|
+
this.logger.warn("All Redis data cleared!");
|
|
281
|
+
}
|
|
282
|
+
catch (error) {
|
|
283
|
+
this.logger.error("Error flushing Redis:", error);
|
|
284
|
+
throw error;
|
|
285
|
+
}
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
getRedisInfo() {
|
|
289
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
290
|
+
try {
|
|
291
|
+
const info = yield this.redis.info();
|
|
292
|
+
const lines = info.split("\n");
|
|
293
|
+
const result = {};
|
|
294
|
+
for (const line of lines) {
|
|
295
|
+
if (line.includes(":")) {
|
|
296
|
+
const [key, value] = line.split(":");
|
|
297
|
+
result[key.trim()] = value.trim();
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
return result;
|
|
301
|
+
}
|
|
302
|
+
catch (error) {
|
|
303
|
+
this.logger.error("Error getting Redis info:", error);
|
|
304
|
+
throw error;
|
|
305
|
+
}
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
exports.RedisClient = RedisClient;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uber-clone/common",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.8",
|
|
4
4
|
"main": "./build/index.js",
|
|
5
5
|
"types": "./build/index.d.ts",
|
|
6
6
|
"files": [
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"scripts": {
|
|
10
10
|
"clean": "del-cli ./build/*",
|
|
11
11
|
"build": "npm run clean && tsc",
|
|
12
|
-
"pub": "git add . && git commit -m \"New Updated\" && npm version patch && npm run build && npm publish"
|
|
12
|
+
"pub": "cd common && git add . && git commit -m \"New Updated\" && npm version patch && npm run build && npm publish"
|
|
13
13
|
},
|
|
14
14
|
"keywords": [],
|
|
15
15
|
"author": "",
|
|
@@ -29,6 +29,8 @@
|
|
|
29
29
|
"jsonwebtoken": "^9.0.2",
|
|
30
30
|
"kafkajs": "^2.2.4",
|
|
31
31
|
"prom-client": "^15.1.3",
|
|
32
|
+
"redis": "^5.9.0",
|
|
33
|
+
"redlock": "^5.0.0-beta.2",
|
|
32
34
|
"winston": "^3.18.3"
|
|
33
35
|
}
|
|
34
36
|
}
|