@prashant-neosoft-ecommerce/shared 1.0.3 → 1.0.6
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/middleware/rateLimiter.js +26 -10
- package/middleware/validation.js +3 -0
- package/package.json +1 -1
- package/utils/redis.js +67 -39
|
@@ -3,12 +3,25 @@ const { RedisStore } = require("rate-limit-redis");
|
|
|
3
3
|
const redisClient = require("../utils/redis");
|
|
4
4
|
|
|
5
5
|
const createRateLimiter = (options = {}) => {
|
|
6
|
+
// Check if Redis client is connected
|
|
7
|
+
if (!redisClient.client || !redisClient.isConnected) {
|
|
8
|
+
console.warn("Redis not connected, using memory store for rate limiting");
|
|
9
|
+
return rateLimit({
|
|
10
|
+
windowMs: options.windowMs || 15 * 60 * 1000,
|
|
11
|
+
max: options.max || 100,
|
|
12
|
+
message: options.message || "Too many requests, please try again later.",
|
|
13
|
+
standardHeaders: true,
|
|
14
|
+
legacyHeaders: false,
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
|
|
6
18
|
return rateLimit({
|
|
7
19
|
store: new RedisStore({
|
|
8
|
-
|
|
9
|
-
|
|
20
|
+
// Use sendCommand method from ioredis client
|
|
21
|
+
sendCommand: (...args) => redisClient.client.call(...args),
|
|
22
|
+
prefix: options.prefix || "rl:",
|
|
10
23
|
}),
|
|
11
|
-
windowMs: options.windowMs || 15 * 60 * 1000,
|
|
24
|
+
windowMs: options.windowMs || 15 * 60 * 1000,
|
|
12
25
|
max: options.max || 100,
|
|
13
26
|
message: options.message || "Too many requests, please try again later.",
|
|
14
27
|
standardHeaders: true,
|
|
@@ -16,13 +29,16 @@ const createRateLimiter = (options = {}) => {
|
|
|
16
29
|
});
|
|
17
30
|
};
|
|
18
31
|
|
|
19
|
-
|
|
20
|
-
const standardLimiter = createRateLimiter({ max: 100 });
|
|
21
|
-
const generousLimiter = createRateLimiter({ max: 1000 });
|
|
22
|
-
|
|
32
|
+
// Export factory functions that create limiters on-demand
|
|
23
33
|
module.exports = {
|
|
24
34
|
createRateLimiter,
|
|
25
|
-
strictLimiter
|
|
26
|
-
|
|
27
|
-
|
|
35
|
+
get strictLimiter() {
|
|
36
|
+
return createRateLimiter({ max: 5, windowMs: 60 * 1000 });
|
|
37
|
+
},
|
|
38
|
+
get standardLimiter() {
|
|
39
|
+
return createRateLimiter({ max: 100 });
|
|
40
|
+
},
|
|
41
|
+
get generousLimiter() {
|
|
42
|
+
return createRateLimiter({ max: 1000 });
|
|
43
|
+
},
|
|
28
44
|
};
|
package/middleware/validation.js
CHANGED
|
@@ -40,6 +40,9 @@ const schemas = {
|
|
|
40
40
|
stock: Joi.number().integer().min(0).required(),
|
|
41
41
|
category: Joi.string().required(),
|
|
42
42
|
sku: Joi.string().required(),
|
|
43
|
+
images: Joi.array().items(Joi.string().uri()).optional(),
|
|
44
|
+
isActive: Joi.boolean().optional(),
|
|
45
|
+
metadata: Joi.object().unknown(true).optional(),
|
|
43
46
|
}),
|
|
44
47
|
|
|
45
48
|
order: Joi.object({
|
package/package.json
CHANGED
package/utils/redis.js
CHANGED
|
@@ -7,64 +7,92 @@ class RedisClient {
|
|
|
7
7
|
this.isConnected = false;
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
connect() {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
return this.client;
|
|
34
|
-
}
|
|
10
|
+
async connect() {
|
|
11
|
+
try {
|
|
12
|
+
this.client = new Redis({
|
|
13
|
+
host: process.env.REDIS_HOST || "localhost",
|
|
14
|
+
port: process.env.REDIS_PORT || 6379,
|
|
15
|
+
retryStrategy: (times) => {
|
|
16
|
+
const delay = Math.min(times * 50, 2000);
|
|
17
|
+
return delay;
|
|
18
|
+
},
|
|
19
|
+
lazyConnect: false, // Connect immediately
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
this.client.on("connect", () => {
|
|
23
|
+
this.isConnected = true;
|
|
24
|
+
logger.info("Redis connected successfully");
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
this.client.on("ready", () => {
|
|
28
|
+
this.isConnected = true;
|
|
29
|
+
logger.info("Redis ready to accept commands");
|
|
30
|
+
});
|
|
35
31
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
32
|
+
this.client.on("error", (err) => {
|
|
33
|
+
this.isConnected = false;
|
|
34
|
+
logger.error("Redis error:", err);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
this.client.on("close", () => {
|
|
38
|
+
this.isConnected = false;
|
|
39
|
+
logger.warn("Redis connection closed");
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
// Wait for connection to be ready
|
|
43
|
+
await this.client.ping();
|
|
44
|
+
|
|
45
|
+
return this.client;
|
|
46
|
+
} catch (error) {
|
|
47
|
+
this.isConnected = false;
|
|
48
|
+
logger.error("Redis connection failed:", error);
|
|
49
|
+
// Don't throw error, allow app to continue without Redis
|
|
50
|
+
return null;
|
|
39
51
|
}
|
|
40
|
-
return this.client;
|
|
41
52
|
}
|
|
42
53
|
|
|
43
54
|
async get(key) {
|
|
55
|
+
if (!this.isConnected) return null;
|
|
56
|
+
|
|
44
57
|
try {
|
|
45
|
-
return await this.
|
|
46
|
-
} catch (
|
|
47
|
-
logger.error(
|
|
58
|
+
return await this.client.get(key);
|
|
59
|
+
} catch (error) {
|
|
60
|
+
logger.error(`Redis GET error for key ${key}:`, error);
|
|
48
61
|
return null;
|
|
49
62
|
}
|
|
50
63
|
}
|
|
51
64
|
|
|
52
|
-
async set(key, value,
|
|
65
|
+
async set(key, value, expireSeconds = 300) {
|
|
66
|
+
if (!this.isConnected) return;
|
|
67
|
+
|
|
53
68
|
try {
|
|
54
69
|
if (typeof value === "object") {
|
|
55
70
|
value = JSON.stringify(value);
|
|
56
71
|
}
|
|
57
|
-
await this.
|
|
58
|
-
} catch (
|
|
59
|
-
logger.error(
|
|
72
|
+
await this.client.set(key, value, "EX", expireSeconds);
|
|
73
|
+
} catch (error) {
|
|
74
|
+
logger.error(`Redis SET error for key ${key}:`, error);
|
|
60
75
|
}
|
|
61
76
|
}
|
|
62
77
|
|
|
63
78
|
async del(key) {
|
|
79
|
+
if (!this.isConnected) return;
|
|
80
|
+
|
|
81
|
+
try {
|
|
82
|
+
await this.client.del(key);
|
|
83
|
+
} catch (error) {
|
|
84
|
+
logger.error(`Redis DEL error for key ${key}:`, error);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async exists(key) {
|
|
89
|
+
if (!this.isConnected) return false;
|
|
90
|
+
|
|
64
91
|
try {
|
|
65
|
-
await this.
|
|
66
|
-
} catch (
|
|
67
|
-
logger.error(
|
|
92
|
+
return await this.client.exists(key);
|
|
93
|
+
} catch (error) {
|
|
94
|
+
logger.error(`Redis EXISTS error for key ${key}:`, error);
|
|
95
|
+
return false;
|
|
68
96
|
}
|
|
69
97
|
}
|
|
70
98
|
}
|