dt-common-device 3.0.0 → 3.0.1
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/README.md +47 -15
- package/dist/device/cloud/interface.d.ts +101 -0
- package/dist/device/cloud/interface.js +3 -0
- package/dist/device/cloud/interfaces/IDeviceConnectionService.d.ts +7 -0
- package/dist/device/cloud/interfaces/IDeviceConnectionService.js +3 -0
- package/dist/device/cloud/interfaces/IDevicesService.d.ts +9 -0
- package/dist/device/cloud/interfaces/IDevicesService.js +2 -0
- package/dist/device/cloud/services/Device.service.d.ts +39 -0
- package/dist/device/cloud/services/Device.service.js +9 -0
- package/dist/device/cloud/services/DeviceCloudService.d.ts +42 -0
- package/dist/device/cloud/services/DeviceCloudService.js +59 -0
- package/dist/device/cloud/services/DeviceHub.service.d.ts +3 -0
- package/dist/device/cloud/services/DeviceHub.service.js +6 -0
- package/dist/device/cloud/services/Hub.service.d.ts +25 -0
- package/dist/device/cloud/services/Hub.service.js +9 -0
- package/dist/device/cloud/services/SmartThingsDeviceService.d.ts +38 -0
- package/dist/device/cloud/services/SmartThingsDeviceService.js +52 -0
- package/dist/device/index.d.ts +4 -0
- package/dist/device/index.js +20 -0
- package/dist/device/local/events/EventHandler.js +6 -6
- package/dist/device/local/events/Events.d.ts +12 -33
- package/dist/device/local/events/Events.js +12 -33
- package/dist/device/local/interface.d.ts +0 -0
- package/dist/device/local/interface.js +1 -0
- package/dist/device/local/services/DeviceHub.service.d.ts +11 -0
- package/dist/device/local/services/DeviceHub.service.js +40 -0
- package/dist/queue/entities/HybridHttpQueue.d.ts +4 -3
- package/dist/queue/entities/HybridHttpQueue.js +95 -43
- package/dist/queue/interfaces/IHybridHttpQueue.d.ts +3 -2
- package/dist/queue/interfaces/IJobResult.d.ts +8 -0
- package/dist/queue/services/QueueService.d.ts +3 -3
- package/dist/queue/types/queue.types.d.ts +0 -4
- package/dist/queue/utils/queueUtils.js +3 -2
- package/dist/queue/utils/rateLimit.utils.d.ts +4 -0
- package/dist/queue/utils/rateLimit.utils.js +54 -1
- package/package.json +1 -1
- package/src/queue/entities/HybridHttpQueue.ts +140 -64
- package/src/queue/interfaces/IHybridHttpQueue.ts +3 -2
- package/src/queue/interfaces/IJobResult.ts +9 -0
- package/src/queue/services/QueueService.ts +3 -3
- package/src/queue/types/queue.types.ts +0 -1
- package/src/queue/utils/queueUtils.ts +3 -2
- package/src/queue/utils/rateLimit.utils.ts +74 -1
|
@@ -3,17 +3,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.DT_EVENT_TYPES = void 0;
|
|
4
4
|
exports.DT_EVENT_TYPES = {
|
|
5
5
|
DEVICE: {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
UPDATE: {
|
|
11
|
-
SUCCESS: "device.update.success",
|
|
12
|
-
FAILED: "device.update.failed",
|
|
13
|
-
},
|
|
14
|
-
DELETE: {
|
|
15
|
-
SUCCESS: "device.delete.success",
|
|
16
|
-
FAILED: "device.delete.failed",
|
|
6
|
+
DEVICE: {
|
|
7
|
+
CREATED: "device.device.created",
|
|
8
|
+
UPDATED: "device.device.updated",
|
|
9
|
+
DELETED: "device.device.deleted",
|
|
17
10
|
},
|
|
18
11
|
STATE: {
|
|
19
12
|
SET: "device.state.set",
|
|
@@ -44,31 +37,17 @@ exports.DT_EVENT_TYPES = {
|
|
|
44
37
|
},
|
|
45
38
|
},
|
|
46
39
|
CONNECTION: {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
UPDATE: {
|
|
52
|
-
SUCCESS: "connection.update.success",
|
|
53
|
-
FAILED: "connection.update.failed",
|
|
54
|
-
},
|
|
55
|
-
DELETE: {
|
|
56
|
-
SUCCESS: "connection.delete.success",
|
|
57
|
-
FAILED: "connection.delete.failed",
|
|
40
|
+
CONNECTION: {
|
|
41
|
+
CREATED: "connection.connection.created",
|
|
42
|
+
UPDATED: "connection.connection.updated",
|
|
43
|
+
DELETED: "connection.connection.deleted",
|
|
58
44
|
},
|
|
59
45
|
},
|
|
60
46
|
PROPERTY: {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
UPDATE: {
|
|
66
|
-
SUCCESS: "property.update.success",
|
|
67
|
-
FAILED: "property.update.failed",
|
|
68
|
-
},
|
|
69
|
-
DELETE: {
|
|
70
|
-
SUCCESS: "property.delete.success",
|
|
71
|
-
FAILED: "property.delete.failed",
|
|
47
|
+
PROPERTY: {
|
|
48
|
+
CREATED: "property.property.created",
|
|
49
|
+
UPDATED: "property.property.updated",
|
|
50
|
+
DELETED: "property.property.deleted",
|
|
72
51
|
},
|
|
73
52
|
PREFERENCES: {
|
|
74
53
|
UPDATED: "property.preferences.updated",
|
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { IHubCreateParams } from "../interfaces";
|
|
2
|
+
export declare class DeviceHubService {
|
|
3
|
+
private readonly baseUrl;
|
|
4
|
+
constructor();
|
|
5
|
+
addHub(body: IHubCreateParams): Promise<any>;
|
|
6
|
+
getHubs(hubIds: string[]): Promise<any>;
|
|
7
|
+
getHub(hubId: string): Promise<any>;
|
|
8
|
+
updateHub(hubId: string, body: any): Promise<any>;
|
|
9
|
+
deleteHub(hubId: string): Promise<any>;
|
|
10
|
+
deleteAllHubs(hubIds: string[]): Promise<any>;
|
|
11
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
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.DeviceHubService = void 0;
|
|
7
|
+
const axios_1 = __importDefault(require("axios"));
|
|
8
|
+
const config_1 = require("../../../config/config");
|
|
9
|
+
class DeviceHubService {
|
|
10
|
+
constructor() {
|
|
11
|
+
const { DEVICE_SERVICE } = (0, config_1.getConfig)();
|
|
12
|
+
if (!DEVICE_SERVICE) {
|
|
13
|
+
throw new Error("DEVICE_SERVICE is not configured. Call initialize() first with DEVICE_SERVICE.");
|
|
14
|
+
}
|
|
15
|
+
this.baseUrl = DEVICE_SERVICE;
|
|
16
|
+
}
|
|
17
|
+
async addHub(body) {
|
|
18
|
+
return await axios_1.default.post(`${this.baseUrl}/devices/hubs`, body);
|
|
19
|
+
}
|
|
20
|
+
//get hubs takes an array of hub ids as query params
|
|
21
|
+
async getHubs(hubIds) {
|
|
22
|
+
const query = hubIds && hubIds.length ? `?ids=${hubIds.join(",")}` : "";
|
|
23
|
+
return await axios_1.default.get(`${this.baseUrl}/devices/hubs${query}`);
|
|
24
|
+
}
|
|
25
|
+
//get hub takes a hub id in params
|
|
26
|
+
async getHub(hubId) {
|
|
27
|
+
return await axios_1.default.get(`${this.baseUrl}/devices/hubs/${hubId}`);
|
|
28
|
+
}
|
|
29
|
+
async updateHub(hubId, body) {
|
|
30
|
+
return await axios_1.default.put(`${this.baseUrl}/devices/hubs/${hubId}`, body);
|
|
31
|
+
}
|
|
32
|
+
async deleteHub(hubId) {
|
|
33
|
+
return await axios_1.default.delete(`${this.baseUrl}/devices/hubs/${hubId}`);
|
|
34
|
+
}
|
|
35
|
+
async deleteAllHubs(hubIds) {
|
|
36
|
+
const query = hubIds.length ? `?ids=${hubIds.join(",")}` : "";
|
|
37
|
+
return await axios_1.default.delete(`${this.baseUrl}/devices/hubs${query}`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
exports.DeviceHubService = DeviceHubService;
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { HttpCallOption } from "../types/http.types";
|
|
2
|
+
import { IQueueResponse } from "../interfaces";
|
|
2
3
|
export declare class HybridHttpQueue {
|
|
3
4
|
private readonly queues;
|
|
4
5
|
private readonly workers;
|
|
5
6
|
private readonly rateLimitConfigs;
|
|
6
7
|
private readonly jobResults;
|
|
7
8
|
constructor();
|
|
8
|
-
private
|
|
9
|
+
private handleRateLimitAndQueue;
|
|
9
10
|
private processHttpRequest;
|
|
10
11
|
request(options: {
|
|
11
12
|
method: string;
|
|
@@ -17,7 +18,7 @@ export declare class HybridHttpQueue {
|
|
|
17
18
|
connectionProvider: string;
|
|
18
19
|
microservice: string;
|
|
19
20
|
};
|
|
20
|
-
}): Promise<
|
|
21
|
-
handleRequest(url: string, method: string, options: HttpCallOption): Promise<
|
|
21
|
+
}): Promise<IQueueResponse>;
|
|
22
|
+
handleRequest(url: string, method: string, options: HttpCallOption): Promise<IQueueResponse>;
|
|
22
23
|
shutdown(): Promise<void>;
|
|
23
24
|
}
|
|
@@ -61,44 +61,69 @@ let HybridHttpQueue = (() => {
|
|
|
61
61
|
this.jobResults = new Map();
|
|
62
62
|
this.rateLimitConfigs = rateLimit_utils_1.RateLimitUtils.initializeRateLimitConfigs();
|
|
63
63
|
}
|
|
64
|
-
async
|
|
65
|
-
const { connectionId, provider,
|
|
66
|
-
const
|
|
67
|
-
const
|
|
64
|
+
async handleRateLimitAndQueue(url, method, options) {
|
|
65
|
+
const { connectionId, provider, microservice } = jobUtils_1.JobUtils.extractConnectionDetails(options);
|
|
66
|
+
const key = `rate_limit:${provider}:${connectionId}`;
|
|
67
|
+
const config = this.rateLimitConfigs.get(provider);
|
|
68
|
+
const windowMs = config?.windowMs ?? 60000;
|
|
69
|
+
const timestamps = await rateLimit_utils_1.RateLimitUtils.getRawRequestTimestamps(key);
|
|
70
|
+
const now = Date.now();
|
|
71
|
+
const windowStart = now - windowMs;
|
|
72
|
+
const recentRequests = timestamps.filter((t) => t > windowStart);
|
|
73
|
+
const nextAvailableTime = recentRequests.length > 0 ? recentRequests[0] + windowMs : now + 1000;
|
|
74
|
+
const delay = Math.max(nextAvailableTime - now, 1000); // at least 1s delay
|
|
75
|
+
// Create job data
|
|
76
|
+
const jobData = {
|
|
77
|
+
microservice,
|
|
78
|
+
connectionId,
|
|
79
|
+
provider,
|
|
80
|
+
url,
|
|
81
|
+
method,
|
|
82
|
+
options,
|
|
83
|
+
timestamp: Date.now(),
|
|
84
|
+
};
|
|
85
|
+
// Add job to queue with delay (background processing)
|
|
86
|
+
const queueKey = queueUtils_1.QueueUtils.getQueueKey(microservice, connectionId, provider);
|
|
87
|
+
const queue = queueUtils_1.QueueUtils.getOrCreateQueue(queueKey, this.queues);
|
|
88
|
+
queueUtils_1.QueueUtils.getOrCreateWorker(queueKey, this.workers, this.processHttpRequest.bind(this), this.jobResults);
|
|
89
|
+
const job = await queue.add("http-request", jobData, {
|
|
90
|
+
delay,
|
|
91
|
+
attempts: 1,
|
|
92
|
+
removeOnComplete: { age: 300, count: 1 },
|
|
93
|
+
removeOnFail: { age: 300, count: 1 },
|
|
94
|
+
});
|
|
68
95
|
await (0, dt_audit_library_1.publishAudit)({
|
|
69
|
-
eventType:
|
|
70
|
-
? "http.request.failed"
|
|
71
|
-
: "http.request.rateLimitExceeded",
|
|
96
|
+
eventType: "http.request.rateLimitQueued",
|
|
72
97
|
properties: {
|
|
98
|
+
resource: microservice,
|
|
73
99
|
connectionId,
|
|
74
100
|
provider,
|
|
75
101
|
endpoint: url,
|
|
76
102
|
method,
|
|
77
103
|
timestamp: Date.now(),
|
|
78
104
|
queueId: job.id,
|
|
79
|
-
reason:
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
maxRetries: 3,
|
|
83
|
-
}),
|
|
105
|
+
reason: "rate_limit_exceeded_queued",
|
|
106
|
+
delay,
|
|
107
|
+
estimatedProcessingTime: now + delay,
|
|
84
108
|
},
|
|
85
109
|
});
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
}
|
|
93
|
-
throw new Error("Rate limit exceeded");
|
|
110
|
+
// Return immediate response to controller
|
|
111
|
+
return {
|
|
112
|
+
success: true,
|
|
113
|
+
queued: true,
|
|
114
|
+
estimatedProcessingTime: now + delay,
|
|
115
|
+
jobId: job.id,
|
|
116
|
+
};
|
|
94
117
|
}
|
|
95
118
|
async processHttpRequest(job) {
|
|
96
119
|
const { connectionId, provider, url, method, options } = job.data;
|
|
97
|
-
|
|
98
|
-
if (!
|
|
99
|
-
|
|
120
|
+
const allowed = await rateLimit_utils_1.RateLimitUtils.isRateLimitAllowed(connectionId, provider, this.rateLimitConfigs);
|
|
121
|
+
if (!allowed) {
|
|
122
|
+
// This shouldn't happen since we check before queuing, but handle it gracefully
|
|
123
|
+
(0, config_1.getConfig)().LOGGER.warn(`Job ${job.id} still rate limited after delay, skipping`);
|
|
100
124
|
return;
|
|
101
125
|
}
|
|
126
|
+
await rateLimit_utils_1.RateLimitUtils.recordRequest(connectionId, provider);
|
|
102
127
|
try {
|
|
103
128
|
(0, config_1.getConfig)().LOGGER.info(`Executing HTTP request: ${method} ${url} for ${provider}`);
|
|
104
129
|
const response = await (0, axios_1.default)({
|
|
@@ -143,26 +168,53 @@ let HybridHttpQueue = (() => {
|
|
|
143
168
|
}
|
|
144
169
|
async handleRequest(url, method, options) {
|
|
145
170
|
const { connectionId, provider, microservice } = jobUtils_1.JobUtils.extractConnectionDetails(options);
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
171
|
+
// Check rate limit first
|
|
172
|
+
const allowed = await rateLimit_utils_1.RateLimitUtils.isRateLimitAllowed(connectionId, provider, this.rateLimitConfigs);
|
|
173
|
+
if (!allowed) {
|
|
174
|
+
// Rate limited - queue the request and return immediate response
|
|
175
|
+
return this.handleRateLimitAndQueue(url, method, options);
|
|
176
|
+
}
|
|
177
|
+
// Not rate limited - process immediately
|
|
178
|
+
(0, config_1.getConfig)().LOGGER.info(`Processing immediately: ${method} ${url} -> ${provider} [${connectionId}]`);
|
|
179
|
+
try {
|
|
180
|
+
// Record the request first
|
|
181
|
+
await rateLimit_utils_1.RateLimitUtils.recordRequest(connectionId, provider);
|
|
182
|
+
// Execute the HTTP request
|
|
183
|
+
const response = await (0, axios_1.default)({
|
|
184
|
+
method: method.toLowerCase(),
|
|
185
|
+
url: url,
|
|
186
|
+
headers: options.headers || {},
|
|
187
|
+
timeout: 30000,
|
|
188
|
+
...(options.body && { data: options.body }),
|
|
189
|
+
...(options.params && { params: options.params }),
|
|
190
|
+
});
|
|
191
|
+
(0, config_1.getConfig)().LOGGER.info(`HTTP request successful: ${method} ${url} for ${provider}`);
|
|
192
|
+
return {
|
|
193
|
+
success: true,
|
|
194
|
+
data: response.data,
|
|
195
|
+
queued: false,
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
catch (error) {
|
|
199
|
+
(0, config_1.getConfig)().LOGGER.error(`HTTP request failed: ${error.message}`);
|
|
200
|
+
await (0, dt_audit_library_1.publishAudit)({
|
|
201
|
+
eventType: "http.request.error",
|
|
202
|
+
properties: {
|
|
203
|
+
connectionId,
|
|
204
|
+
provider,
|
|
205
|
+
endpoint: url,
|
|
206
|
+
method,
|
|
207
|
+
timestamp: Date.now(),
|
|
208
|
+
reason: "execution_error",
|
|
209
|
+
errorMessage: error.message,
|
|
210
|
+
},
|
|
211
|
+
});
|
|
212
|
+
return {
|
|
213
|
+
success: false,
|
|
214
|
+
error: `HTTP request failed: ${error.message}`,
|
|
215
|
+
queued: false,
|
|
216
|
+
};
|
|
217
|
+
}
|
|
166
218
|
}
|
|
167
219
|
async shutdown() {
|
|
168
220
|
(0, config_1.getConfig)().LOGGER.info("Shutting down HTTP queues...");
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { HttpCallOption } from "../types/http.types";
|
|
2
|
+
import { IQueueResponse } from "./IJobResult";
|
|
2
3
|
export interface IHybridHttpQueue {
|
|
3
4
|
request(options: {
|
|
4
5
|
method: string;
|
|
@@ -10,7 +11,7 @@ export interface IHybridHttpQueue {
|
|
|
10
11
|
connectionProvider: string;
|
|
11
12
|
microservice: string;
|
|
12
13
|
};
|
|
13
|
-
}): Promise<
|
|
14
|
-
handleRequest(url: string, method: string, options: HttpCallOption): Promise<
|
|
14
|
+
}): Promise<IQueueResponse>;
|
|
15
|
+
handleRequest(url: string, method: string, options: HttpCallOption): Promise<IQueueResponse>;
|
|
15
16
|
shutdown(): Promise<void>;
|
|
16
17
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { IHybridHttpQueue } from "../interfaces";
|
|
1
|
+
import { IHybridHttpQueue, IQueueResponse } from "../interfaces";
|
|
2
2
|
import { HttpCallOption } from "../types/http.types";
|
|
3
3
|
export declare class QueueService implements IHybridHttpQueue {
|
|
4
4
|
private readonly hybridQueue;
|
|
@@ -13,7 +13,7 @@ export declare class QueueService implements IHybridHttpQueue {
|
|
|
13
13
|
connectionProvider: string;
|
|
14
14
|
microservice: string;
|
|
15
15
|
};
|
|
16
|
-
}): Promise<
|
|
17
|
-
handleRequest(url: string, method: string, options: HttpCallOption): Promise<
|
|
16
|
+
}): Promise<IQueueResponse>;
|
|
17
|
+
handleRequest(url: string, method: string, options: HttpCallOption): Promise<IQueueResponse>;
|
|
18
18
|
shutdown(): Promise<void>;
|
|
19
19
|
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.QueueUtils = void 0;
|
|
4
4
|
const config_1 = require("../../config/config");
|
|
5
|
+
const redis_1 = require("../../db/redis");
|
|
5
6
|
class QueueUtils {
|
|
6
7
|
static getQueueKey(microservice, connectionId, provider) {
|
|
7
8
|
return `${microservice}_${provider}_${connectionId}`;
|
|
@@ -10,7 +11,7 @@ class QueueUtils {
|
|
|
10
11
|
return (queues.get(queueKey) ??
|
|
11
12
|
queues
|
|
12
13
|
.set(queueKey, new (require("bullmq").Queue)(queueKey, {
|
|
13
|
-
connection:
|
|
14
|
+
connection: (0, redis_1.getRedisClient)(),
|
|
14
15
|
}))
|
|
15
16
|
.get(queueKey));
|
|
16
17
|
}
|
|
@@ -19,7 +20,7 @@ class QueueUtils {
|
|
|
19
20
|
return;
|
|
20
21
|
const { Worker } = require("bullmq");
|
|
21
22
|
const worker = new Worker(queueKey, processFunction, {
|
|
22
|
-
connection:
|
|
23
|
+
connection: (0, redis_1.getRedisClient)(),
|
|
23
24
|
concurrency: 1,
|
|
24
25
|
removeOnComplete: { age: 300, count: 1 },
|
|
25
26
|
removeOnFail: { age: 300, count: 1 },
|
|
@@ -3,4 +3,8 @@ export declare class RateLimitUtils {
|
|
|
3
3
|
private static redisClient;
|
|
4
4
|
static checkRateLimit(connectionId: string, provider: string, rateLimitConfigs: Map<string, IRateLimitConfig>): Promise<boolean>;
|
|
5
5
|
static initializeRateLimitConfigs(): Map<string, IRateLimitConfig>;
|
|
6
|
+
static isRateLimitAllowed(connectionId: string, provider: string, rateLimitConfigs: Map<string, IRateLimitConfig>): Promise<boolean>;
|
|
7
|
+
static recordRequest(connectionId: string, provider: string): Promise<void>;
|
|
8
|
+
static getRawRequestTimestamps(key: string): Promise<number[]>;
|
|
9
|
+
static getRateLimitConfig(provider: string): IRateLimitConfig | undefined;
|
|
6
10
|
}
|
|
@@ -33,12 +33,65 @@ class RateLimitUtils {
|
|
|
33
33
|
const configs = new Map();
|
|
34
34
|
// Configure rate limits for different providers
|
|
35
35
|
configs.set("Sensibo", {
|
|
36
|
-
maxRequests:
|
|
36
|
+
maxRequests: 5,
|
|
37
37
|
windowMs: 60000,
|
|
38
38
|
provider: "Sensibo",
|
|
39
39
|
});
|
|
40
40
|
return configs;
|
|
41
41
|
}
|
|
42
|
+
static async isRateLimitAllowed(connectionId, provider, rateLimitConfigs) {
|
|
43
|
+
const config = rateLimitConfigs.get(provider);
|
|
44
|
+
if (!config) {
|
|
45
|
+
(0, config_1.getConfig)().LOGGER.warn(`No rate limit config found for provider: ${provider}`);
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
const key = `rate_limit:${provider}:${connectionId}`;
|
|
49
|
+
const now = Date.now();
|
|
50
|
+
const windowStart = now - config.windowMs;
|
|
51
|
+
try {
|
|
52
|
+
const data = await this.redisClient.get(key);
|
|
53
|
+
const requests = data
|
|
54
|
+
? JSON.parse(data).filter((t) => t > windowStart)
|
|
55
|
+
: [];
|
|
56
|
+
return requests.length < config.maxRequests;
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
(0, config_1.getConfig)().LOGGER.error(`Rate limit check error: ${error}`);
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
static async recordRequest(connectionId, provider) {
|
|
64
|
+
const config = this.getRateLimitConfig(provider);
|
|
65
|
+
if (!config)
|
|
66
|
+
return;
|
|
67
|
+
const key = `rate_limit:${provider}:${connectionId}`;
|
|
68
|
+
const now = Date.now();
|
|
69
|
+
const windowStart = now - config.windowMs;
|
|
70
|
+
try {
|
|
71
|
+
const data = await this.redisClient.get(key);
|
|
72
|
+
const requests = data
|
|
73
|
+
? JSON.parse(data).filter((t) => t > windowStart)
|
|
74
|
+
: [];
|
|
75
|
+
requests.push(now);
|
|
76
|
+
await this.redisClient.setex(key, Math.ceil(config.windowMs / 1000), JSON.stringify(requests));
|
|
77
|
+
}
|
|
78
|
+
catch (error) {
|
|
79
|
+
(0, config_1.getConfig)().LOGGER.error(`Rate limit record error: ${error}`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
static async getRawRequestTimestamps(key) {
|
|
83
|
+
try {
|
|
84
|
+
const data = await this.redisClient.get(key);
|
|
85
|
+
return data ? JSON.parse(data) : [];
|
|
86
|
+
}
|
|
87
|
+
catch (error) {
|
|
88
|
+
(0, config_1.getConfig)().LOGGER.error(`Error fetching raw request timestamps: ${error}`);
|
|
89
|
+
return [];
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
static getRateLimitConfig(provider) {
|
|
93
|
+
return this.initializeRateLimitConfigs().get(provider);
|
|
94
|
+
}
|
|
42
95
|
}
|
|
43
96
|
exports.RateLimitUtils = RateLimitUtils;
|
|
44
97
|
RateLimitUtils.redisClient = (0, redis_1.getRedisClient)();
|