express-adaptive-rate-limiter 1.0.0 → 2.0.0
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 +250 -16
- package/dist/algorithms/baseAlgorithm.js +1 -1
- package/dist/algorithms/fixedWindow.js +1 -1
- package/dist/algorithms/leakyBucket.js +1 -1
- package/dist/algorithms/slidingWindowCounter.js +1 -1
- package/dist/algorithms/slidingWindowLog.js +1 -1
- package/dist/algorithms/tokenBucket.js +1 -1
- package/dist/core/blockedKeys.js +1 -0
- package/dist/core/coreMiddleware.js +1 -0
- package/dist/core/limiters/clusterLimiter.js +16 -0
- package/dist/core/limiters/dynamoDbLimiter.js +1 -0
- package/dist/core/limiters/etcdLimiter.js +1 -0
- package/dist/core/limiters/memcachedLimiter.js +1 -0
- package/dist/core/limiters/memoryLimiter.js +1 -0
- package/dist/core/limiters/mongoLimiter.js +1 -0
- package/dist/core/limiters/mongooseLimiter.js +1 -0
- package/dist/core/limiters/mySqlLimiter.js +12 -0
- package/dist/core/limiters/nodeClusterLimiter.js +1 -0
- package/dist/core/limiters/postgresLimiter.js +13 -0
- package/dist/core/limiters/prismaLimiter.js +24 -0
- package/dist/core/limiters/redisLimiter.js +16 -0
- package/dist/core/limiters/sqliteLimiter.js +13 -0
- package/dist/core/limiters/valkeyLimiter.js +16 -0
- package/dist/core/rateLimiter.js +1 -0
- package/dist/core/rateLimiterRes.js +1 -0
- package/dist/core/rateLimiterStore.js +1 -0
- package/dist/core/wrappers/blackAndWhiteLimiter.js +1 -0
- package/dist/core/wrappers/burstyRateLimiter.js +1 -0
- package/dist/core/wrappers/rateLimiterQueue.js +1 -0
- package/dist/core/wrappers/rateLimiterUnion.js +1 -0
- package/dist/core/wrappers/timeoutLimiter.js +1 -0
- package/dist/index.js +1 -1
- package/dist/middleware/expressMiddleware.js +1 -1
- package/dist/middleware/headers.js +1 -1
- package/dist/rateLimit.js +1 -1
- package/dist/rateLimitManager.js +1 -1
- package/dist/stores/baseStore.js +1 -1
- package/dist/stores/luaScripts.js +379 -1
- package/dist/stores/memoryStore.js +1 -1
- package/dist/stores/redisStore.js +1 -1
- package/dist/types/index.d.ts +507 -0
- package/dist/utils/errors.js +1 -1
- package/dist/utils/graphQLParser.js +1 -1
- package/dist/utils/ipUtils.js +1 -1
- package/dist/utils/keyGenerator.js +1 -1
- package/dist/utils/redisClient.js +1 -0
- package/dist/utils/retryAfter.js +1 -1
- package/dist/utils/skipUtils.js +1 -1
- package/dist/utils/time.js +1 -1
- package/dist/utils/validation.js +1 -1
- package/package.json +67 -4
|
@@ -1 +1,379 @@
|
|
|
1
|
-
function _0x2e06(_0x48d999,_0x41ffd4){_0x48d999=_0x48d999-326;const _0x38b4d6=_0x38b4();let _0x2e0634=_0x38b4d6[_0x48d999];if(_0x2e06["QIujAb"]===undefined){var _0x116e7d=function(_0x17592f){const _0x182637="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=";let _0x57312c="",_0x1f4116="";for(let _0xea548c=0,_0x2e06ed,_0x2b9380,_0x36a26a=0;_0x2b9380=_0x17592f["charAt"](_0x36a26a++);~_0x2b9380&&(_0x2e06ed=_0xea548c%4?_0x2e06ed*64+_0x2b9380:_0x2b9380,_0xea548c++%4)?_0x57312c+=String["fromCharCode"](255&_0x2e06ed>>(-2*_0xea548c&6)):0){_0x2b9380=_0x182637["indexOf"](_0x2b9380)}for(let _0x26aee9=0,_0x1c3f96=_0x57312c["length"];_0x26aee9<_0x1c3f96;_0x26aee9++){_0x1f4116+="%"+("00"+_0x57312c["charCodeAt"](_0x26aee9)["toString"](16))["slice"](-2)}return decodeURIComponent(_0x1f4116)};_0x2e06["ZmMjrt"]=_0x116e7d,_0x2e06["RQQvlL"]={},_0x2e06["QIujAb"]=!![]}const _0x325962=_0x38b4d6[0];_0x2e06["Pvqlrw"]!==_0x325962&&(_0x2e06["RQQvlL"]={},_0x2e06["Pvqlrw"]=_0x325962);const _0x4763e4=_0x2e06["RQQvlL"][_0x48d999];return _0x4763e4===undefined?(_0x2e0634=_0x2e06["ZmMjrt"](_0x2e0634),_0x2e06["RQQvlL"][_0x48d999]=_0x2e0634):_0x2e0634=_0x4763e4,_0x2e0634}const _0x53dc29=_0x2e06;(function(_0x2bde2c,_0x32351c){const _0xb47721=_0x2e06,_0x87b691=_0x2bde2c();while(!![]){try{const _0x58c220=parseInt(_0xb47721(337))/1+-parseInt(_0xb47721(335))/2*(parseInt(_0xb47721(342))/3)+-parseInt(_0xb47721(326))/4*(parseInt(_0xb47721(333))/5)+-parseInt(_0xb47721(340))/6+-parseInt(_0xb47721(329))/7+-parseInt(_0xb47721(330))/8+parseInt(_0xb47721(338))/9*(parseInt(_0xb47721(331))/10);if(_0x58c220===_0x32351c)break;else _0x87b691["push"](_0x87b691["shift"]())}catch(_0x3eaa66){_0x87b691["push"](_0x87b691["shift"]())}}})(_0x38b4,511188);function _0x38b4(){const _0x5cbfa5=["ouXswwTtuG","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","ntC5mJqZnNLYvvLzAa","C2HHmq","ntC5mJK0sgjsBwfg","mJeXnLLVt21nqW","y3jLyxrLsgfZAa","zw50CMLLCW","nJiYnZm0shnzyw9l","nZe5mJG2nhf3BuP1za","mJKYnty3mZbLDeLuAxi","DxbKyxrL","mZK0mgXpquzbva","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","mMjeDej2yq","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","mtq4otqXwMrhvLn4"];_0x38b4=function(){return _0x5cbfa5};return _0x38b4()}import _0x1f4116 from"crypto";export const fixedWindowScript=_0x53dc29(334);export const slidingWindowLogScript="\nlocal now\nif ARGV[5] and tonumber(ARGV[5]) and tonumber(ARGV[5]) > 0 then\n now = math.floor(tonumber(ARGV[5]))\nelse\n local time_res = redis.call('TIME')\n now = math.floor(tonumber(time_res[1]) * 1000.0 + (tonumber(time_res[2]) / 1000.0))\nend\n\nlocal limit = tonumber(ARGV[1]) * 1.0\nlocal windowMs = tonumber(ARGV[2]) * 1.0\nlocal cost = tonumber(ARGV[3]) * 1.0\nlocal req_id = ARGV[4] or tostring(now)\n\nlocal windowStart = now - windowMs\nredis.call('ZREMRANGEBYSCORE', KEYS[1], '-inf', '(' .. string.format('%.0f', windowStart))\n\nlocal current = tonumber(redis.call('ZCARD', KEYS[1])) or 0.0\n\nif cost == 0 then\n local remaining = math.max(0.0, limit - current)\n local oldest = redis.call('ZRANGE', KEYS[1], 0, 0, 'WITHSCORES')\n local resetTime = now + windowMs\n if oldest and #oldest >= 2 and tonumber(oldest[2]) then\n resetTime = tonumber(oldest[2]) + windowMs\n end\n local isAllowed = 1\n local retryAfter = 0\n if current >= limit then\n isAllowed = 0\n local waitMs = resetTime - now\n if waitMs <= 0 then waitMs = 1 end\n retryAfter = math.ceil(waitMs / 1000.0)\n if retryAfter < 1 then retryAfter = 1 end\n end\n return { isAllowed, limit, remaining, current, retryAfter, string.format('%.0f', resetTime) }\nend\n\nif current + cost <= limit then\n for i = 1, cost do\n redis.call('ZADD', KEYS[1], now, req_id .. ':' .. tostring(i) .. ':' .. tostring(math.random(100000, 999999)))\n end\n redis.call('PEXPIRE', KEYS[1], string.format('%.0f', windowMs))\n local consumed = current + cost\n local remaining = limit - consumed\n local oldest = redis.call('ZRANGE', KEYS[1], 0, 0, 'WITHSCORES')\n local resetTime = now + windowMs\n if oldest and #oldest >= 2 and tonumber(oldest[2]) then\n resetTime = tonumber(oldest[2]) + windowMs\n end\n return { 1, limit, remaining, consumed, 0, string.format('%.0f', resetTime) }\nelse\n local targetIdx = math.floor((current + cost) - limit - 1)\n if targetIdx < 0 then\n targetIdx = 0\n end\n if targetIdx >= current then\n targetIdx = math.max(0, math.floor(current - 1))\n end\n local target = redis.call('ZRANGE', KEYS[1], targetIdx, targetIdx, 'WITHSCORES')\n local expireScore = now\n if target and #target >= 2 and tonumber(target[2]) then\n expireScore = tonumber(target[2])\n end\n local resetTime = expireScore + windowMs\n local waitMs = resetTime - now\n if waitMs <= 0 then\n waitMs = 1\n end\n local retryAfter = math.ceil(waitMs / 1000.0)\n if retryAfter < 1 then\n retryAfter = 1\n end\n local remaining = math.max(0.0, limit - current)\n return { 0, limit, remaining, current, retryAfter, string.format('%.0f', resetTime) }\nend\n";export const slidingWindowCounterScript=_0x53dc29(339);export const tokenBucketScript=_0x53dc29(336);export const leakyBucketScript="\nlocal now\nif ARGV[5] and tonumber(ARGV[5]) and tonumber(ARGV[5]) > 0 then\n now = math.floor(tonumber(ARGV[5]))\nelse\n local time_res = redis.call('TIME')\n now = math.floor(tonumber(time_res[1]) * 1000.0 + (tonumber(time_res[2]) / 1000.0))\nend\n\nlocal capacity = tonumber(ARGV[1]) * 1.0\nlocal leakRate = tonumber(ARGV[2]) * 1.0\nlocal leakInterval = tonumber(ARGV[3]) * 1.0\nlocal cost = tonumber(ARGV[4]) * 1.0\n\nlocal rate = leakRate / leakInterval\n\nlocal data = redis.call('HMGET', KEYS[1], 'water', 'lastLeak')\nlocal water = tonumber(data[1])\nlocal lastLeak = tonumber(data[2])\n\nif not water then\n water = 0.0\n lastLeak = now\nelse\n local elapsed = math.max(0.0, now - lastLeak)\n water = math.max(0.0, water - (elapsed * rate))\n lastLeak = now\nend\n\nlocal pexpire = math.ceil((capacity / rate) * 2.0)\nif pexpire < 1000 then\n pexpire = 1000\nend\n\nif cost == 0 then\n local consumed = math.ceil(water)\n local remaining = math.max(0.0, math.floor(capacity - water))\n local resetTime = now + math.ceil(water / rate)\n local isAllowed = 1\n local retryAfter = 0\n if water >= capacity then\n isAllowed = 0\n local waitMs = math.ceil(1.0 / rate)\n retryAfter = math.ceil(waitMs / 1000.0)\n if retryAfter < 1 then retryAfter = 1 end\n end\n redis.call('HMSET', KEYS[1], 'water', tostring(water), 'lastLeak', string.format('%.0f', lastLeak))\n redis.call('PEXPIRE', KEYS[1], string.format('%.0f', pexpire))\n return { isAllowed, capacity, remaining, consumed, retryAfter, string.format('%.0f', resetTime), 0 }\nend\n\nif (water + cost) <= capacity then\n water = water + cost\n local consumed = math.ceil(water)\n local remaining = math.max(0.0, math.floor(capacity - water))\n local resetTime = now + math.ceil(water / rate)\n redis.call('HMSET', KEYS[1], 'water', tostring(water), 'lastLeak', string.format('%.0f', lastLeak))\n redis.call('PEXPIRE', KEYS[1], string.format('%.0f', pexpire))\n return { 1, capacity, remaining, consumed, 0, string.format('%.0f', resetTime), 0 }\nelse\n local consumed = math.ceil(water)\n local remaining = math.max(0.0, math.floor(capacity - water))\n local deficit = (water + cost) - capacity\n local waitMs = math.ceil(deficit / rate)\n local retryAfter = math.ceil(waitMs / 1000.0)\n if retryAfter < 1 then\n retryAfter = 1\n end\n local resetTime = now + math.ceil(water / rate)\n redis.call('HMSET', KEYS[1], 'water', tostring(water), 'lastLeak', string.format('%.0f', lastLeak))\n redis.call('PEXPIRE', KEYS[1], string.format('%.0f', pexpire))\n return { 0, capacity, remaining, consumed, retryAfter, string.format('%.0f', resetTime), waitMs }\nend\n";export const SCRIPTS={"fixed-window":fixedWindowScript,"sliding-window-log":slidingWindowLogScript,"sliding-window-counter":slidingWindowCounterScript,"token-bucket":tokenBucketScript,"leaky-bucket":leakyBucketScript};export const SCRIPT_SHAS=Object["fromEntries"](Object[_0x53dc29(328)](SCRIPTS)["map"](([_0xea548c,_0x2e06ed])=>[_0xea548c,_0x1f4116[_0x53dc29(327)](_0x53dc29(341))[_0x53dc29(332)](_0x2e06ed)["digest"]("hex")]));
|
|
1
|
+
import crypto from'crypto';export const fixedWindowScript=`
|
|
2
|
+
local now
|
|
3
|
+
if ARGV[4] and tonumber(ARGV[4]) and tonumber(ARGV[4]) > 0 then
|
|
4
|
+
now = math.floor(tonumber(ARGV[4]))
|
|
5
|
+
else
|
|
6
|
+
local time_res = redis.call('TIME')
|
|
7
|
+
now = math.floor(tonumber(time_res[1]) * 1000.0 + (tonumber(time_res[2]) / 1000.0))
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
local limit = tonumber(ARGV[1]) * 1.0
|
|
11
|
+
local windowMs = tonumber(ARGV[2]) * 1.0
|
|
12
|
+
local cost = tonumber(ARGV[3]) * 1.0
|
|
13
|
+
|
|
14
|
+
local data = redis.call('HMGET', KEYS[1], 'count', 'windowStart', 'resetTime')
|
|
15
|
+
local storedCount = tonumber(data[1]) or 0
|
|
16
|
+
local storedWindow = tonumber(data[2]) or 0
|
|
17
|
+
local storedReset = tonumber(data[3]) or 0
|
|
18
|
+
|
|
19
|
+
-- The window is anchored on the first request of a key and never slides, so a
|
|
20
|
+
-- client that straddles a wall clock boundary cannot consume two windows in
|
|
21
|
+
-- quick succession.
|
|
22
|
+
local current
|
|
23
|
+
local windowStart
|
|
24
|
+
local resetTime
|
|
25
|
+
if storedReset > now then
|
|
26
|
+
current = storedCount
|
|
27
|
+
windowStart = storedWindow
|
|
28
|
+
resetTime = storedReset
|
|
29
|
+
else
|
|
30
|
+
current = 0
|
|
31
|
+
windowStart = now
|
|
32
|
+
resetTime = now + windowMs
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
local pttl = math.floor(resetTime - now)
|
|
36
|
+
if pttl <= 0 then
|
|
37
|
+
pttl = 1
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
if cost == 0 then
|
|
41
|
+
local remaining = math.max(0, limit - current)
|
|
42
|
+
local isAllowed = 1
|
|
43
|
+
local retryAfter = 0
|
|
44
|
+
if current >= limit then
|
|
45
|
+
isAllowed = 0
|
|
46
|
+
retryAfter = math.ceil(pttl / 1000.0)
|
|
47
|
+
if retryAfter < 1 then retryAfter = 1 end
|
|
48
|
+
end
|
|
49
|
+
return { isAllowed, limit, remaining, current, retryAfter, string.format('%.0f', resetTime) }
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
if current + cost <= limit then
|
|
53
|
+
local nextCount = current + cost
|
|
54
|
+
local remaining = limit - nextCount
|
|
55
|
+
redis.call('HMSET', KEYS[1], 'count', string.format('%.0f', nextCount), 'windowStart', string.format('%.0f', windowStart), 'resetTime', string.format('%.0f', resetTime))
|
|
56
|
+
redis.call('PEXPIRE', KEYS[1], string.format('%.0f', pttl))
|
|
57
|
+
return { 1, limit, remaining, nextCount, 0, string.format('%.0f', resetTime) }
|
|
58
|
+
else
|
|
59
|
+
local remaining = math.max(0, limit - current)
|
|
60
|
+
local retryAfter = math.ceil(pttl / 1000.0)
|
|
61
|
+
if retryAfter < 1 then
|
|
62
|
+
retryAfter = 1
|
|
63
|
+
end
|
|
64
|
+
return { 0, limit, remaining, current, retryAfter, string.format('%.0f', resetTime) }
|
|
65
|
+
end
|
|
66
|
+
`;export const slidingWindowLogScript=`
|
|
67
|
+
local now
|
|
68
|
+
if ARGV[5] and tonumber(ARGV[5]) and tonumber(ARGV[5]) > 0 then
|
|
69
|
+
now = math.floor(tonumber(ARGV[5]))
|
|
70
|
+
else
|
|
71
|
+
local time_res = redis.call('TIME')
|
|
72
|
+
now = math.floor(tonumber(time_res[1]) * 1000.0 + (tonumber(time_res[2]) / 1000.0))
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
local limit = tonumber(ARGV[1]) * 1.0
|
|
76
|
+
local windowMs = tonumber(ARGV[2]) * 1.0
|
|
77
|
+
local cost = tonumber(ARGV[3]) * 1.0
|
|
78
|
+
local req_id = ARGV[4] or tostring(now)
|
|
79
|
+
|
|
80
|
+
local windowStart = now - windowMs
|
|
81
|
+
redis.call('ZREMRANGEBYSCORE', KEYS[1], '-inf', '(' .. string.format('%.0f', windowStart))
|
|
82
|
+
|
|
83
|
+
local current = tonumber(redis.call('ZCARD', KEYS[1])) or 0.0
|
|
84
|
+
|
|
85
|
+
if cost == 0 then
|
|
86
|
+
local remaining = math.max(0.0, limit - current)
|
|
87
|
+
local oldest = redis.call('ZRANGE', KEYS[1], 0, 0, 'WITHSCORES')
|
|
88
|
+
local resetTime = now + windowMs
|
|
89
|
+
if oldest and #oldest >= 2 and tonumber(oldest[2]) then
|
|
90
|
+
resetTime = tonumber(oldest[2]) + windowMs
|
|
91
|
+
end
|
|
92
|
+
local isAllowed = 1
|
|
93
|
+
local retryAfter = 0
|
|
94
|
+
if current >= limit then
|
|
95
|
+
isAllowed = 0
|
|
96
|
+
local waitMs = resetTime - now
|
|
97
|
+
if waitMs <= 0 then waitMs = 1 end
|
|
98
|
+
retryAfter = math.ceil(waitMs / 1000.0)
|
|
99
|
+
if retryAfter < 1 then retryAfter = 1 end
|
|
100
|
+
end
|
|
101
|
+
return { isAllowed, limit, remaining, current, retryAfter, string.format('%.0f', resetTime) }
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
if current + cost <= limit then
|
|
105
|
+
for i = 1, cost do
|
|
106
|
+
redis.call('ZADD', KEYS[1], now, req_id .. ':' .. tostring(i) .. ':' .. tostring(math.random(100000, 999999)))
|
|
107
|
+
end
|
|
108
|
+
redis.call('PEXPIRE', KEYS[1], string.format('%.0f', windowMs))
|
|
109
|
+
local consumed = current + cost
|
|
110
|
+
local remaining = limit - consumed
|
|
111
|
+
local oldest = redis.call('ZRANGE', KEYS[1], 0, 0, 'WITHSCORES')
|
|
112
|
+
local resetTime = now + windowMs
|
|
113
|
+
if oldest and #oldest >= 2 and tonumber(oldest[2]) then
|
|
114
|
+
resetTime = tonumber(oldest[2]) + windowMs
|
|
115
|
+
end
|
|
116
|
+
return { 1, limit, remaining, consumed, 0, string.format('%.0f', resetTime) }
|
|
117
|
+
else
|
|
118
|
+
local targetIdx = math.floor((current + cost) - limit - 1)
|
|
119
|
+
if targetIdx < 0 then
|
|
120
|
+
targetIdx = 0
|
|
121
|
+
end
|
|
122
|
+
if targetIdx >= current then
|
|
123
|
+
targetIdx = math.max(0, math.floor(current - 1))
|
|
124
|
+
end
|
|
125
|
+
local target = redis.call('ZRANGE', KEYS[1], targetIdx, targetIdx, 'WITHSCORES')
|
|
126
|
+
local expireScore = now
|
|
127
|
+
if target and #target >= 2 and tonumber(target[2]) then
|
|
128
|
+
expireScore = tonumber(target[2])
|
|
129
|
+
end
|
|
130
|
+
local resetTime = expireScore + windowMs
|
|
131
|
+
local waitMs = resetTime - now
|
|
132
|
+
if waitMs <= 0 then
|
|
133
|
+
waitMs = 1
|
|
134
|
+
end
|
|
135
|
+
local retryAfter = math.ceil(waitMs / 1000.0)
|
|
136
|
+
if retryAfter < 1 then
|
|
137
|
+
retryAfter = 1
|
|
138
|
+
end
|
|
139
|
+
local remaining = math.max(0.0, limit - current)
|
|
140
|
+
return { 0, limit, remaining, current, retryAfter, string.format('%.0f', resetTime) }
|
|
141
|
+
end
|
|
142
|
+
`;export const slidingWindowCounterScript=`
|
|
143
|
+
local now
|
|
144
|
+
if ARGV[4] and tonumber(ARGV[4]) and tonumber(ARGV[4]) > 0 then
|
|
145
|
+
now = math.floor(tonumber(ARGV[4]))
|
|
146
|
+
else
|
|
147
|
+
local time_res = redis.call('TIME')
|
|
148
|
+
now = math.floor(tonumber(time_res[1]) * 1000.0 + (tonumber(time_res[2]) / 1000.0))
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
local limit = tonumber(ARGV[1]) * 1.0
|
|
152
|
+
local windowMs = tonumber(ARGV[2]) * 1.0
|
|
153
|
+
local cost = tonumber(ARGV[3]) * 1.0
|
|
154
|
+
|
|
155
|
+
local currWindowStart = math.floor(now / windowMs) * windowMs
|
|
156
|
+
local resetTime = currWindowStart + windowMs
|
|
157
|
+
|
|
158
|
+
local data = redis.call('HMGET', KEYS[1], 'windowStart', 'prevCount', 'currCount')
|
|
159
|
+
local storedWindow = tonumber(data[1])
|
|
160
|
+
local prevCount = tonumber(data[2]) or 0.0
|
|
161
|
+
local currCount = tonumber(data[3]) or 0.0
|
|
162
|
+
|
|
163
|
+
if not storedWindow then
|
|
164
|
+
storedWindow = currWindowStart
|
|
165
|
+
prevCount = 0.0
|
|
166
|
+
currCount = 0.0
|
|
167
|
+
elseif currWindowStart == storedWindow then
|
|
168
|
+
-- same window
|
|
169
|
+
elseif currWindowStart == (storedWindow + windowMs) then
|
|
170
|
+
prevCount = currCount
|
|
171
|
+
currCount = 0.0
|
|
172
|
+
storedWindow = currWindowStart
|
|
173
|
+
else
|
|
174
|
+
prevCount = 0.0
|
|
175
|
+
currCount = 0.0
|
|
176
|
+
storedWindow = currWindowStart
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
local elapsed = now - currWindowStart
|
|
180
|
+
local weight = math.max(0.0, 1.0 - (elapsed / windowMs))
|
|
181
|
+
local estimated = (prevCount * weight) + currCount
|
|
182
|
+
|
|
183
|
+
if cost == 0 then
|
|
184
|
+
local consumed = math.floor(estimated + 0.5)
|
|
185
|
+
local remaining = math.max(0.0, limit - consumed)
|
|
186
|
+
local isAllowed = 1
|
|
187
|
+
local retryAfter = 0
|
|
188
|
+
if estimated >= limit then
|
|
189
|
+
isAllowed = 0
|
|
190
|
+
local waitMs = resetTime - now
|
|
191
|
+
if prevCount > 0 then
|
|
192
|
+
local neededWeight = (limit - currCount) / prevCount
|
|
193
|
+
if neededWeight < 1.0 and neededWeight >= 0.0 then
|
|
194
|
+
local t = windowMs * (1.0 - neededWeight)
|
|
195
|
+
waitMs = math.max(1.0, math.ceil(t - elapsed))
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
if waitMs <= 0 or waitMs > windowMs then
|
|
199
|
+
waitMs = resetTime - now
|
|
200
|
+
end
|
|
201
|
+
retryAfter = math.ceil(waitMs / 1000.0)
|
|
202
|
+
if retryAfter < 1 then retryAfter = 1 end
|
|
203
|
+
end
|
|
204
|
+
return { isAllowed, limit, remaining, consumed, retryAfter, string.format('%.0f', resetTime) }
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
if (estimated + cost) <= limit then
|
|
208
|
+
currCount = currCount + cost
|
|
209
|
+
local consumed = math.floor(estimated + cost + 0.5)
|
|
210
|
+
local remaining = math.max(0.0, limit - consumed)
|
|
211
|
+
redis.call('HMSET', KEYS[1], 'windowStart', string.format('%.0f', storedWindow), 'prevCount', string.format('%.0f', prevCount), 'currCount', string.format('%.0f', currCount))
|
|
212
|
+
redis.call('PEXPIRE', KEYS[1], string.format('%.0f', windowMs * 2.0))
|
|
213
|
+
return { 1, limit, remaining, consumed, 0, string.format('%.0f', resetTime) }
|
|
214
|
+
else
|
|
215
|
+
local consumed = math.floor(estimated + 0.5)
|
|
216
|
+
local remaining = math.max(0.0, limit - consumed)
|
|
217
|
+
local waitMs = resetTime - now
|
|
218
|
+
if prevCount > 0 then
|
|
219
|
+
local neededWeight = (limit - currCount - cost) / prevCount
|
|
220
|
+
if neededWeight < 1.0 and neededWeight >= 0.0 then
|
|
221
|
+
local t = windowMs * (1.0 - neededWeight)
|
|
222
|
+
waitMs = math.max(1.0, math.ceil(t - elapsed))
|
|
223
|
+
end
|
|
224
|
+
end
|
|
225
|
+
if waitMs <= 0 or waitMs > windowMs then
|
|
226
|
+
waitMs = resetTime - now
|
|
227
|
+
end
|
|
228
|
+
local retryAfter = math.ceil(waitMs / 1000.0)
|
|
229
|
+
if retryAfter < 1 then
|
|
230
|
+
retryAfter = 1
|
|
231
|
+
end
|
|
232
|
+
redis.call('HMSET', KEYS[1], 'windowStart', string.format('%.0f', storedWindow), 'prevCount', string.format('%.0f', prevCount), 'currCount', string.format('%.0f', currCount))
|
|
233
|
+
redis.call('PEXPIRE', KEYS[1], string.format('%.0f', windowMs * 2.0))
|
|
234
|
+
return { 0, limit, remaining, consumed, retryAfter, string.format('%.0f', resetTime) }
|
|
235
|
+
end
|
|
236
|
+
`;export const tokenBucketScript=`
|
|
237
|
+
local now
|
|
238
|
+
if ARGV[6] and tonumber(ARGV[6]) and tonumber(ARGV[6]) > 0 then
|
|
239
|
+
now = math.floor(tonumber(ARGV[6]))
|
|
240
|
+
else
|
|
241
|
+
local time_res = redis.call('TIME')
|
|
242
|
+
now = math.floor(tonumber(time_res[1]) * 1000.0 + (tonumber(time_res[2]) / 1000.0))
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
local capacity = tonumber(ARGV[1]) * 1.0
|
|
246
|
+
local refillRate = tonumber(ARGV[2]) * 1.0
|
|
247
|
+
local refillInterval = tonumber(ARGV[3]) * 1.0
|
|
248
|
+
local cost = tonumber(ARGV[4]) * 1.0
|
|
249
|
+
local initialTokens = (tonumber(ARGV[5]) or capacity) * 1.0
|
|
250
|
+
|
|
251
|
+
local rate = refillRate / refillInterval
|
|
252
|
+
|
|
253
|
+
local data = redis.call('HMGET', KEYS[1], 'tokens', 'lastRefill')
|
|
254
|
+
local tokens = tonumber(data[1]) or initialTokens
|
|
255
|
+
local lastRefill = tonumber(data[2]) or now
|
|
256
|
+
|
|
257
|
+
local elapsed = math.max(0.0, now - lastRefill)
|
|
258
|
+
tokens = math.min(capacity, tokens + (elapsed * rate))
|
|
259
|
+
lastRefill = now
|
|
260
|
+
|
|
261
|
+
local pexpire = math.ceil((capacity / rate) * 2.0)
|
|
262
|
+
if pexpire < 1000 then
|
|
263
|
+
pexpire = 1000
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
if cost == 0 then
|
|
267
|
+
local consumed = capacity - math.floor(tokens)
|
|
268
|
+
local remaining = math.floor(tokens)
|
|
269
|
+
local resetTime = now + math.ceil((capacity - tokens) / rate)
|
|
270
|
+
local isAllowed = 1
|
|
271
|
+
local retryAfter = 0
|
|
272
|
+
if tokens < 1.0 then
|
|
273
|
+
isAllowed = 0
|
|
274
|
+
local deficit = 1.0 - tokens
|
|
275
|
+
local waitMs = math.ceil(deficit / rate)
|
|
276
|
+
retryAfter = math.ceil(waitMs / 1000.0)
|
|
277
|
+
if retryAfter < 1 then retryAfter = 1 end
|
|
278
|
+
end
|
|
279
|
+
redis.call('HMSET', KEYS[1], 'tokens', tostring(tokens), 'lastRefill', string.format('%.0f', lastRefill))
|
|
280
|
+
redis.call('PEXPIRE', KEYS[1], string.format('%.0f', pexpire))
|
|
281
|
+
return { isAllowed, capacity, remaining, consumed, retryAfter, string.format('%.0f', resetTime) }
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
if tokens >= cost then
|
|
285
|
+
tokens = tokens - cost
|
|
286
|
+
local consumed = capacity - math.floor(tokens)
|
|
287
|
+
local remaining = math.floor(tokens)
|
|
288
|
+
local resetTime = now + math.ceil((capacity - tokens) / rate)
|
|
289
|
+
redis.call('HMSET', KEYS[1], 'tokens', tostring(tokens), 'lastRefill', string.format('%.0f', lastRefill))
|
|
290
|
+
redis.call('PEXPIRE', KEYS[1], string.format('%.0f', pexpire))
|
|
291
|
+
return { 1, capacity, remaining, consumed, 0, string.format('%.0f', resetTime) }
|
|
292
|
+
else
|
|
293
|
+
local consumed = capacity - math.floor(tokens)
|
|
294
|
+
local remaining = math.floor(tokens)
|
|
295
|
+
local deficit = cost - tokens
|
|
296
|
+
local waitMs = math.ceil(deficit / rate)
|
|
297
|
+
local retryAfter = math.ceil(waitMs / 1000.0)
|
|
298
|
+
if retryAfter < 1 then
|
|
299
|
+
retryAfter = 1
|
|
300
|
+
end
|
|
301
|
+
local resetTime = now + math.ceil((capacity - tokens) / rate)
|
|
302
|
+
redis.call('HMSET', KEYS[1], 'tokens', tostring(tokens), 'lastRefill', string.format('%.0f', lastRefill))
|
|
303
|
+
redis.call('PEXPIRE', KEYS[1], string.format('%.0f', pexpire))
|
|
304
|
+
return { 0, capacity, remaining, consumed, retryAfter, string.format('%.0f', resetTime) }
|
|
305
|
+
end
|
|
306
|
+
`;export const leakyBucketScript=`
|
|
307
|
+
local now
|
|
308
|
+
if ARGV[5] and tonumber(ARGV[5]) and tonumber(ARGV[5]) > 0 then
|
|
309
|
+
now = math.floor(tonumber(ARGV[5]))
|
|
310
|
+
else
|
|
311
|
+
local time_res = redis.call('TIME')
|
|
312
|
+
now = math.floor(tonumber(time_res[1]) * 1000.0 + (tonumber(time_res[2]) / 1000.0))
|
|
313
|
+
end
|
|
314
|
+
|
|
315
|
+
local capacity = tonumber(ARGV[1]) * 1.0
|
|
316
|
+
local leakRate = tonumber(ARGV[2]) * 1.0
|
|
317
|
+
local leakInterval = tonumber(ARGV[3]) * 1.0
|
|
318
|
+
local cost = tonumber(ARGV[4]) * 1.0
|
|
319
|
+
|
|
320
|
+
local rate = leakRate / leakInterval
|
|
321
|
+
|
|
322
|
+
local data = redis.call('HMGET', KEYS[1], 'water', 'lastLeak')
|
|
323
|
+
local water = tonumber(data[1])
|
|
324
|
+
local lastLeak = tonumber(data[2])
|
|
325
|
+
|
|
326
|
+
if not water then
|
|
327
|
+
water = 0.0
|
|
328
|
+
lastLeak = now
|
|
329
|
+
else
|
|
330
|
+
local elapsed = math.max(0.0, now - lastLeak)
|
|
331
|
+
water = math.max(0.0, water - (elapsed * rate))
|
|
332
|
+
lastLeak = now
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
local pexpire = math.ceil((capacity / rate) * 2.0)
|
|
336
|
+
if pexpire < 1000 then
|
|
337
|
+
pexpire = 1000
|
|
338
|
+
end
|
|
339
|
+
|
|
340
|
+
if cost == 0 then
|
|
341
|
+
local consumed = math.ceil(water)
|
|
342
|
+
local remaining = math.max(0.0, math.floor(capacity - water))
|
|
343
|
+
local resetTime = now + math.ceil(water / rate)
|
|
344
|
+
local isAllowed = 1
|
|
345
|
+
local retryAfter = 0
|
|
346
|
+
if water >= capacity then
|
|
347
|
+
isAllowed = 0
|
|
348
|
+
local waitMs = math.ceil(1.0 / rate)
|
|
349
|
+
retryAfter = math.ceil(waitMs / 1000.0)
|
|
350
|
+
if retryAfter < 1 then retryAfter = 1 end
|
|
351
|
+
end
|
|
352
|
+
redis.call('HMSET', KEYS[1], 'water', tostring(water), 'lastLeak', string.format('%.0f', lastLeak))
|
|
353
|
+
redis.call('PEXPIRE', KEYS[1], string.format('%.0f', pexpire))
|
|
354
|
+
return { isAllowed, capacity, remaining, consumed, retryAfter, string.format('%.0f', resetTime), 0 }
|
|
355
|
+
end
|
|
356
|
+
|
|
357
|
+
if (water + cost) <= capacity then
|
|
358
|
+
water = water + cost
|
|
359
|
+
local consumed = math.ceil(water)
|
|
360
|
+
local remaining = math.max(0.0, math.floor(capacity - water))
|
|
361
|
+
local resetTime = now + math.ceil(water / rate)
|
|
362
|
+
redis.call('HMSET', KEYS[1], 'water', tostring(water), 'lastLeak', string.format('%.0f', lastLeak))
|
|
363
|
+
redis.call('PEXPIRE', KEYS[1], string.format('%.0f', pexpire))
|
|
364
|
+
return { 1, capacity, remaining, consumed, 0, string.format('%.0f', resetTime), 0 }
|
|
365
|
+
else
|
|
366
|
+
local consumed = math.ceil(water)
|
|
367
|
+
local remaining = math.max(0.0, math.floor(capacity - water))
|
|
368
|
+
local deficit = (water + cost) - capacity
|
|
369
|
+
local waitMs = math.ceil(deficit / rate)
|
|
370
|
+
local retryAfter = math.ceil(waitMs / 1000.0)
|
|
371
|
+
if retryAfter < 1 then
|
|
372
|
+
retryAfter = 1
|
|
373
|
+
end
|
|
374
|
+
local resetTime = now + math.ceil(water / rate)
|
|
375
|
+
redis.call('HMSET', KEYS[1], 'water', tostring(water), 'lastLeak', string.format('%.0f', lastLeak))
|
|
376
|
+
redis.call('PEXPIRE', KEYS[1], string.format('%.0f', pexpire))
|
|
377
|
+
return { 0, capacity, remaining, consumed, retryAfter, string.format('%.0f', resetTime), waitMs }
|
|
378
|
+
end
|
|
379
|
+
`;export const SCRIPTS={'fixed-window':fixedWindowScript,'sliding-window-log':slidingWindowLogScript,'sliding-window-counter':slidingWindowCounterScript,'token-bucket':tokenBucketScript,'leaky-bucket':leakyBucketScript};export const SCRIPT_SHAS=Object.fromEntries(Object.entries(SCRIPTS).map(([name,script])=>[name,crypto.createHash('sha1').update(script).digest('hex')]));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
import{BaseStore}from'./baseStore.js';class MinHeap{constructor(){this.heap=[];}size(){return this.heap.length;}peek(){return this.heap[0]||null;}push(item){this.heap.push(item);this._bubbleUp(this.heap.length-1);}pop(){if(this.heap.length===0)return null;const top=this.heap[0];const bottom=this.heap.pop();if(this.heap.length>0){this.heap[0]=bottom;this._sinkDown(0);}return top;}_bubbleUp(index){const item=this.heap[index];while(index>0){const parentIdx=Math.floor((index-1)/2);const parent=this.heap[parentIdx];if(item.expiresAt>=parent.expiresAt)break;this.heap[index]=parent;this.heap[parentIdx]=item;index=parentIdx;}}_sinkDown(index){const length=this.heap.length;const item=this.heap[index];while(true){const leftIdx=2*index+1;const rightIdx=2*index+2;let swapIdx=null;if(leftIdx<length&&this.heap[leftIdx].expiresAt<item.expiresAt){swapIdx=leftIdx;}if(rightIdx<length){if(swapIdx===null&&this.heap[rightIdx].expiresAt<item.expiresAt||swapIdx!==null&&this.heap[rightIdx].expiresAt<this.heap[leftIdx].expiresAt){swapIdx=rightIdx;}}if(swapIdx===null)break;this.heap[index]=this.heap[swapIdx];this.heap[swapIdx]=item;index=swapIdx;}}clear(){this.heap=[];}}class ExpirationTracker{constructor(options={}){this.heap=new MinHeap();this.cleanupIntervalMs=options.cleanupIntervalMs||5000;this.timer=null;}start(onExpire){if(this.timer)return;this.timer=setInterval(()=>{this.purgeExpired(Date.now(),onExpire);},this.cleanupIntervalMs);if(this.timer.unref){this.timer.unref();}}stop(){if(this.timer){clearInterval(this.timer);this.timer=null;}}schedule(key,expiresAt){this.heap.push({key,expiresAt});}purgeExpired(now,onExpire){while(this.heap.size()>0){const top=this.heap.peek();if(!top||top.expiresAt>now){break;}this.heap.pop();if(onExpire){onExpire(top.key,top.expiresAt);}}}clear(){this.heap.clear();}}export class MemoryStore extends BaseStore{constructor(options={}){super();this.maxEntries=options.maxEntries||10000;this.cleanupIntervalMs=options.cleanupIntervalMs||5000;this.data=new Map();this.tracker=new ExpirationTracker({cleanupIntervalMs:this.cleanupIntervalMs});this.tracker.start(key=>{const entry=this.data.get(key);if(entry&&entry.expiresAt<=Date.now()){this.data.delete(key);}});}async get(key){const entry=this.data.get(key);if(!entry)return null;if(entry.expiresAt&&entry.expiresAt<=Date.now()){this.data.delete(key);return null;}this.data.delete(key);this.data.set(key,entry);return entry.value;}async set(key,value,ttlMs){const now=Date.now();const expiresAt=ttlMs&&ttlMs>0?now+ttlMs:null;const existing=this.data.get(key);const isNew=existing===undefined;if(isNew&&this.data.size>=this.maxEntries){this.tracker.purgeExpired(now,expiredKey=>{const item=this.data.get(expiredKey);if(item&&item.expiresAt<=now){this.data.delete(expiredKey);}});if(this.data.size>=this.maxEntries){for(const oldestKey of this.data.keys()){this.data.delete(oldestKey);break;}}}this.data.delete(key);this.data.set(key,{value,expiresAt});if(expiresAt&&(isNew||existing.expiresAt!==expiresAt)){this.tracker.schedule(key,expiresAt);}return true;}async delete(key){return this.data.delete(key);}async clear(){this.data.clear();this.tracker.clear();}async close(){this.tracker.stop();this.data.clear();}size(){return this.data.size;}}export default MemoryStore;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
const _0x5285a6=_0x3abe;(function(_0x1693d8,_0x35ab96){const _0xd2cb9b=_0x3abe,_0x37d2cd=_0x1693d8();while(!![]){try{const _0x5c73b4=parseInt(_0xd2cb9b(206))/1+-parseInt(_0xd2cb9b(190))/2+-parseInt(_0xd2cb9b(143))/3+-parseInt(_0xd2cb9b(164))/4+parseInt(_0xd2cb9b(141))/5+parseInt(_0xd2cb9b(197))/6*(parseInt(_0xd2cb9b(156))/7)+parseInt(_0xd2cb9b(181))/8;if(_0x5c73b4===_0x35ab96)break;else _0x37d2cd["push"](_0x37d2cd["shift"]())}catch(_0x5a235d){_0x37d2cd["push"](_0x37d2cd["shift"]())}}})(_0x163c,139662);import{createRequire}from"module";function _0x3abe(_0xe757fc,_0x52c8bb){_0xe757fc=_0xe757fc-135;const _0x163cbd=_0x163c();let _0x3abee0=_0x163cbd[_0xe757fc];if(_0x3abe["MsDhQP"]===undefined){var _0x257efc=function(_0xf82ef5){const _0x7709de="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=";let _0x2bc285="",_0x48d94e="";for(let _0x19f609=0,_0x15ba6f,_0x343464,_0x4c17ea=0;_0x343464=_0xf82ef5["charAt"](_0x4c17ea++);~_0x343464&&(_0x15ba6f=_0x19f609%4?_0x15ba6f*64+_0x343464:_0x343464,_0x19f609++%4)?_0x2bc285+=String["fromCharCode"](255&_0x15ba6f>>(-2*_0x19f609&6)):0){_0x343464=_0x7709de["indexOf"](_0x343464)}for(let _0x3411b1=0,_0x342c8b=_0x2bc285["length"];_0x3411b1<_0x342c8b;_0x3411b1++){_0x48d94e+="%"+("00"+_0x2bc285["charCodeAt"](_0x3411b1)["toString"](16))["slice"](-2)}return decodeURIComponent(_0x48d94e)};_0x3abe["urSASd"]=_0x257efc,_0x3abe["aJOWaM"]={},_0x3abe["MsDhQP"]=!![]}const _0x3a01d9=_0x163cbd[0];_0x3abe["NgbrQc"]!==_0x3a01d9&&(_0x3abe["aJOWaM"]={},_0x3abe["NgbrQc"]=_0x3a01d9);const _0x3670fc=_0x3abe["aJOWaM"][_0xe757fc];return _0x3670fc===undefined?(_0x3abee0=_0x3abe["urSASd"](_0x3abee0),_0x3abe["aJOWaM"][_0xe757fc]=_0x3abee0):_0x3abee0=_0x3670fc,_0x3abee0}function _0x163c(){const _0x4e0487=["uMvKAxmGy2XLyxiGzxjYB3i6ia","CgfYC2u","C2v0","BgvUz3rO","AxnjBNrLCM5HBenSAwvUDa","rvzbta","Aw5JBhvKzxm","C3rHDhvZ","zNvUy3rPB24","nZC2mZCWsLvRBgT1","zgLZy29UBMvJDa","mZuXmdu0u2virKjK","uMvKAxmGzgvSigvYCM9YoIa","uMvKAxmGy2XPzw50igLZig5VDcbJB25MAwD1CMvKig9Yigf2ywLSywjSzs4GugXLyxnLigLUC3rHBgWGiMLVCMvKAxmIig9YicjYzwrPCYiSig9YihbYB3zPzguGyw4Gywn0AxzLignSAwvUDcbPBNn0yw5Jzsb2AweGDgHLicjJBgLLBNqIig9WDgLVBI4","zw5K","y29UC3rYDwn0B3i","C2vUzenVBw1HBMq","BwfW","zgvS","CMW6","BMfTzq","zMfPBe9Wzw4","A2v5CW","uMvKAxmGz2v0igvYCM9YoIa","mtCYnJLKAxj5uxm","AxndB25Uzwn0zwq","z2v0tM93","y3jLyxrLq2XPzw50","tK9tq1jjufq","y2XPzw50","A2v5uhjLzML4","s0vzuW","nJGWnZuYBgD3wgHO","zgf0yq","AxnszwfKEq","Aw9YzwrPCW","zxzHBhnOyq","C2nYAxb0CW","B25fCNjVCG","AxnnB2nR","AxnpCgvU","zxzHBa","uMvKAxmGy2XPzw50igrVzxmGBM90ihn1ChbVCNqGzxzHBcWGzxzHBhnOysWGB3iGC2vUzenVBw1HBMq","zgvMyxvSDa","BM93","CNvUu2nYAxb0","uMvKAxm","BwvZC2fNzq","zxjYB3i","mtaWndiWofnsB0jQrG","x1jLzgLZtw9JAW","C3rVCMvqCMvMAxG","ChjLzML4","y29UBMvJDa","y2f0y2G","CMvHzhK","DxjS","CxvPDa","mtu3mdG4twTHsLDt","thvHihnJCMLWDcaI","B2jQzwn0","x3bYzwzPEeTLEq","y2XLyxi","r0vu","C2nYAxb0u2HHCW","mtG2sKnbvK1z","rvzbtfniqq","u0vu","z2v0","CMvKAxm","uMvKAxnnB2nR","DxnLuMvKAxnuAw1L","x2LUAxrdBgLLBNq","C3rYAw5NAwz5","mtq4mtm1CKzHvfjs"];_0x163c=function(){return _0x4e0487};return _0x163c()}import{BaseStore}from"./baseStore.js";import{RedisConnectionError}from"../utils/errors.js";import{SCRIPTS,SCRIPT_SHAS}from"./luaScripts.js";const require=createRequire(import.meta.url);export class RedisStore extends BaseStore{constructor(_0x48d94e={}){const _0x488b66=_0x3abe;super(),this["keyPrefix"]=_0x48d94e[_0x488b66(162)]||_0x48d94e[_0x488b66(184)]||_0x48d94e[_0x488b66(183)]||_0x48d94e["_storePrefix"]||_0x488b66(151),this[_0x488b66(153)]=Boolean(_0x48d94e[_0x488b66(153)]||_0x48d94e["passOnStoreError"]),this[_0x488b66(169)]=SCRIPTS,this["scriptShas"]=SCRIPT_SHAS,this[_0x488b66(136)]=![],this[_0x488b66(157)]=![],this[_0x488b66(204)](_0x48d94e),this["isMock"]=Boolean(_0x48d94e[_0x488b66(171)]||this[_0x488b66(161)]?.[_0x488b66(165)]instanceof Map||this[_0x488b66(161)]?.[_0x488b66(147)]?.[_0x488b66(152)]===_0x488b66(202)||this[_0x488b66(161)]?.[_0x488b66(147)]?.[_0x488b66(152)]===_0x488b66(182)),this[_0x488b66(203)]=_0x48d94e[_0x488b66(203)]!==undefined?Boolean(_0x48d94e[_0x488b66(203)]):!this[_0x488b66(171)]}[_0x5285a6(158)](){const _0x2ec556=_0x5285a6;return this[_0x2ec556(203)]?0:Date[_0x2ec556(176)]()}["_initClient"](_0x19f609){const _0x428877=_0x5285a6;let _0x15ba6f=_0x19f609[_0x428877(161)]||(_0x19f609[_0x428877(201)]&&typeof _0x19f609[_0x428877(201)][_0x428877(173)]==="function"?_0x19f609["redis"]:null)||(_0x19f609[_0x428877(201)]&&typeof _0x19f609[_0x428877(201)][_0x428877(148)]===_0x428877(140)?_0x19f609[_0x428877(201)]:null)||_0x19f609["redis"]&&_0x19f609[_0x428877(201)][_0x428877(161)]||null;!_0x15ba6f&&typeof _0x19f609[_0x428877(148)]===_0x428877(140)&&(_0x15ba6f={"sendCommand":_0x19f609[_0x428877(148)]});if(!_0x15ba6f){const _0x343464=typeof _0x19f609["redis"]===_0x428877(192)&&_0x19f609[_0x428877(201)]!==null?{..._0x19f609["redis"]}:{},_0x4c17ea=_0x19f609[_0x428877(188)]||_0x343464[_0x428877(188)];let _0x3411b1=_0x19f609[_0x428877(178)]||_0x343464["Redis"];if(!_0x3411b1)try{const _0x342c8b=require(_0x428877(167));_0x3411b1=_0x342c8b[_0x428877(175)]||_0x342c8b}catch{}if(_0x3411b1&&typeof _0x3411b1===_0x428877(140))try{_0x15ba6f=_0x4c17ea?new _0x3411b1(_0x4c17ea,_0x343464):new _0x3411b1(_0x343464),this[_0x428877(136)]=!![]}catch{_0x15ba6f=null}if(!_0x15ba6f)try{const _0x3c340a=require("redis"),_0x1eac7e=_0x3c340a[_0x428877(159)]||_0x3c340a["default"]?.[_0x428877(159)];if(typeof _0x1eac7e===_0x428877(140)){const _0x3d7c55=_0x4c17ea?{"url":_0x4c17ea,..._0x343464}:_0x343464;_0x15ba6f=_0x1eac7e(_0x3d7c55),typeof _0x15ba6f[_0x428877(185)]==="function"&&_0x15ba6f[_0x428877(185)]()[_0x428877(186)](_0x1e8836=>{const _0x41ef19=_0x428877;this[_0x41ef19(157)]=![],typeof _0x19f609["onError"]===_0x41ef19(140)&&_0x19f609["onError"](new RedisConnectionError(_0x1e8836[_0x41ef19(179)],_0x1e8836))}),this[_0x428877(136)]=!![]}}catch{}}this[_0x428877(161)]=_0x15ba6f,this[_0x428877(161)]&&typeof this[_0x428877(161)]["on"]===_0x428877(140)&&(this[_0x428877(161)]["on"]("connect",()=>{this["isConnected"]=!![]}),this[_0x428877(161)]["on"](_0x428877(187),()=>{const _0x47a232=_0x428877;this[_0x47a232(157)]=!![]}),this[_0x428877(161)]["on"](_0x428877(180),_0x3387c4=>{const _0x4f7463=_0x428877;this[_0x4f7463(157)]=![],typeof _0x19f609[_0x4f7463(170)]===_0x4f7463(140)&&_0x19f609[_0x4f7463(170)](new RedisConnectionError(_0x3387c4[_0x4f7463(179)],_0x3387c4))}),this[_0x428877(161)]["on"]("close",()=>{const _0x579078=_0x428877;this[_0x579078(157)]=![]}),this["client"]["on"](_0x428877(146),()=>{this["isConnected"]=![]})),this[_0x428877(161)]&&(this["client"][_0x428877(139)]===_0x428877(187)||this[_0x428877(161)][_0x428877(139)]===_0x428877(185)||this["client"][_0x428877(166)]||this[_0x428877(161)][_0x428877(172)])&&(this[_0x428877(157)]=!![])}["_prefixKey"](_0x4334eb){const _0x4ff8c7=_0x5285a6;return""+this[_0x4ff8c7(162)]+_0x4334eb}async[_0x5285a6(177)](_0x26817a,_0x5175cb=[],_0x12754b=[]){const _0x1e79d9=_0x5285a6,_0x39788f=this[_0x1e79d9(169)][_0x26817a],_0x1c320f=this[_0x1e79d9(196)][_0x26817a];if(!_0x39788f)throw new Error(_0x1e79d9(191)+_0x26817a+"\" not found");if(!this["client"])throw new RedisConnectionError(_0x1e79d9(145));const _0x78bf9f=_0x5175cb[_0x1e79d9(149)](_0x69ff95=>this[_0x1e79d9(193)](_0x69ff95)),_0x366a9e=_0x12754b["map"](_0x530f91=>String(_0x530f91));try{if(typeof this[_0x1e79d9(161)][_0x1e79d9(168)]===_0x1e79d9(140))try{return await this[_0x1e79d9(161)]["evalsha"](_0x1c320f,_0x78bf9f["length"],..._0x78bf9f,..._0x366a9e)}catch(_0xfe0bfd){if(_0xfe0bfd[_0x1e79d9(179)]&&_0xfe0bfd[_0x1e79d9(179)][_0x1e79d9(138)](_0x1e79d9(160)))return await this[_0x1e79d9(161)][_0x1e79d9(173)](_0x39788f,_0x78bf9f[_0x1e79d9(135)],..._0x78bf9f,..._0x366a9e);throw _0xfe0bfd}if(typeof this[_0x1e79d9(161)]["eval"]===_0x1e79d9(140)){if(typeof this[_0x1e79d9(161)][_0x1e79d9(173)][_0x1e79d9(135)]==="number"&&this[_0x1e79d9(161)][_0x1e79d9(173)][_0x1e79d9(135)]<=1)return await this["client"]["eval"](_0x39788f,{"keys":_0x78bf9f,"arguments":_0x366a9e});return await this[_0x1e79d9(161)][_0x1e79d9(173)](_0x39788f,_0x78bf9f[_0x1e79d9(135)],..._0x78bf9f,..._0x366a9e)}if(typeof this[_0x1e79d9(161)][_0x1e79d9(148)]===_0x1e79d9(140))try{return await this[_0x1e79d9(161)]["sendCommand"]([_0x1e79d9(198),_0x1c320f,String(_0x78bf9f[_0x1e79d9(135)]),..._0x78bf9f,..._0x366a9e])}catch(_0x302cc1){if(_0x302cc1[_0x1e79d9(179)]&&_0x302cc1["message"][_0x1e79d9(138)](_0x1e79d9(160)))return await this[_0x1e79d9(161)][_0x1e79d9(148)]([_0x1e79d9(137),_0x39788f,String(_0x78bf9f[_0x1e79d9(135)]),..._0x78bf9f,..._0x366a9e]);throw _0x302cc1}throw new Error(_0x1e79d9(174))}catch(_0x142d3f){throw new RedisConnectionError("Redis script execution error: "+_0x142d3f[_0x1e79d9(179)],_0x142d3f)}}async[_0x5285a6(200)](_0x57215b){const _0x10c30a=_0x5285a6;try{const _0x26159f=this["_prefixKey"](_0x57215b);let _0x2752fc;if(typeof this[_0x10c30a(161)]?.[_0x10c30a(200)]==="function")_0x2752fc=await this[_0x10c30a(161)][_0x10c30a(200)](_0x26159f);else typeof this["client"]?.[_0x10c30a(148)]===_0x10c30a(140)&&(_0x2752fc=await this[_0x10c30a(161)]["sendCommand"]([_0x10c30a(195),_0x26159f]));if(!_0x2752fc)return null;try{return JSON[_0x10c30a(208)](_0x2752fc)}catch{return _0x2752fc}}catch(_0x1e7a2e){throw new RedisConnectionError(_0x10c30a(155)+_0x1e7a2e["message"],_0x1e7a2e)}}async["set"](_0xa65221,_0x239e75,_0x4e7bce){const _0x54def5=_0x5285a6;try{const _0x271e67=this[_0x54def5(193)](_0xa65221),_0x7346dc=typeof _0x239e75===_0x54def5(192)?JSON[_0x54def5(205)](_0x239e75):String(_0x239e75);if(typeof this[_0x54def5(161)]?.[_0x54def5(209)]==="function")_0x4e7bce&&_0x4e7bce>0?await this[_0x54def5(161)][_0x54def5(209)](_0x271e67,_0x7346dc,"PX",_0x4e7bce):await this[_0x54def5(161)][_0x54def5(209)](_0x271e67,_0x7346dc);else typeof this[_0x54def5(161)]?.[_0x54def5(148)]===_0x54def5(140)&&(_0x4e7bce&&_0x4e7bce>0?await this[_0x54def5(161)][_0x54def5(148)]([_0x54def5(199),_0x271e67,_0x7346dc,"PX",String(_0x4e7bce)]):await this[_0x54def5(161)][_0x54def5(148)]([_0x54def5(199),_0x271e67,_0x7346dc]));return!![]}catch(_0x460164){throw new RedisConnectionError("Redis set error: "+_0x460164[_0x54def5(179)],_0x460164)}}async["delete"](_0x29dbd8){const _0x2bb236=_0x5285a6;try{const _0x5a78c6=this[_0x2bb236(193)](_0x29dbd8);let _0x2a070d=0;if(typeof this[_0x2bb236(161)]?.["del"]===_0x2bb236(140))_0x2a070d=await this["client"][_0x2bb236(150)](_0x5a78c6);else typeof this["client"]?.[_0x2bb236(148)]===_0x2bb236(140)&&(_0x2a070d=await this[_0x2bb236(161)][_0x2bb236(148)](["DEL",_0x5a78c6]));return _0x2a070d>0}catch(_0x357951){throw new RedisConnectionError(_0x2bb236(144)+_0x357951["message"],_0x357951)}}async[_0x5285a6(194)](){const _0x2ec9a1=_0x5285a6;try{const _0xe5e3e5=this[_0x2ec9a1(162)]+"*";let _0x11a827=[];if(typeof this["client"]?.[_0x2ec9a1(154)]===_0x2ec9a1(140))_0x11a827=await this[_0x2ec9a1(161)][_0x2ec9a1(154)](_0xe5e3e5);else typeof this[_0x2ec9a1(161)]?.["sendCommand"]==="function"&&(_0x11a827=await this["client"][_0x2ec9a1(148)]([_0x2ec9a1(163),_0xe5e3e5]));if(_0x11a827&&_0x11a827[_0x2ec9a1(135)]>0){if(typeof this["client"]?.[_0x2ec9a1(150)]==="function")await this[_0x2ec9a1(161)][_0x2ec9a1(150)](..._0x11a827);else typeof this[_0x2ec9a1(161)]?.[_0x2ec9a1(148)]===_0x2ec9a1(140)&&(await this["client"]["sendCommand"](["DEL",..._0x11a827]))}}catch(_0x5bbead){throw new RedisConnectionError(_0x2ec9a1(207)+_0x5bbead[_0x2ec9a1(179)],_0x5bbead)}}async["close"](){const _0x8c9a=_0x5285a6;if(this[_0x8c9a(136)]&&this[_0x8c9a(161)]){if(typeof this[_0x8c9a(161)]["quit"]===_0x8c9a(140))await this[_0x8c9a(161)][_0x8c9a(189)]();else typeof this[_0x8c9a(161)][_0x8c9a(142)]===_0x8c9a(140)&&this[_0x8c9a(161)]["disconnect"]()}}}export default RedisStore;
|
|
1
|
+
import{createRequire}from'module';import{BaseStore}from'./baseStore.js';import{RedisConnectionError}from'../utils/errors.js';import{RedisClientAdapter}from'../utils/redisClient.js';import{SCRIPTS,SCRIPT_SHAS}from'./luaScripts.js';const require=createRequire(import.meta.url);export class RedisStore extends BaseStore{constructor(options={}){super();this.keyPrefix=options.keyPrefix||options.prefix||options.storePrefix||options._storePrefix||'rl:';this.failOpen=Boolean(options.failOpen||options.passOnStoreError);this.scripts=SCRIPTS;this.scriptShas=SCRIPT_SHAS;this.isInternalClient=false;this.isConnected=false;this._initClient(options);this.isMock=Boolean(options.isMock||this.client?.data instanceof Map||this.client?.constructor?.name==='RedisMock'||this.client?.constructor?.name==='_RedisMock');this.useRedisTime=options.useRedisTime!==undefined?Boolean(options.useRedisTime):!this.isMock;}getNow(){return this.useRedisTime?0:Date.now();}_initClient(options){let client=options.client||(options.redis&&typeof options.redis.eval==='function'?options.redis:null)||(options.redis&&typeof options.redis.sendCommand==='function'?options.redis:null)||options.redis&&options.redis.client||null;if(!client&&typeof options.sendCommand==='function'){client={sendCommand:options.sendCommand};}if(!client){const redisOptions=typeof options.redis==='object'&&options.redis!==null?{...options.redis}:{};const url=options.url||redisOptions.url;let Redis=options.Redis||redisOptions.Redis;if(!Redis){try{const ioredis=require('ioredis');Redis=ioredis.default||ioredis;}catch{}}if(Redis&&typeof Redis==='function'){try{client=url?new Redis(url,redisOptions):new Redis(redisOptions);this.isInternalClient=true;}catch{client=null;}}if(!client){try{const redisPkg=require('redis');const createClient=redisPkg.createClient||redisPkg.default?.createClient;if(typeof createClient==='function'){const clientOpts=url?{url,...redisOptions}:redisOptions;client=createClient(clientOpts);if(typeof client.connect==='function'){client.connect().catch(err=>{this.isConnected=false;if(typeof options.onError==='function'){options.onError(new RedisConnectionError(err.message,err));}});}this.isInternalClient=true;}}catch{}}}this.client=client;this.adapter=new RedisClientAdapter(client);if(this.client&&typeof this.client.on==='function'){this.client.on('connect',()=>{this.isConnected=true;});this.client.on('ready',()=>{this.isConnected=true;});this.client.on('error',err=>{this.isConnected=false;if(typeof options.onError==='function'){options.onError(new RedisConnectionError(err.message,err));}});this.client.on('close',()=>{this.isConnected=false;});this.client.on('end',()=>{this.isConnected=false;});}if(this.client&&(this.client.status==='ready'||this.client.status==='connect'||this.client.isReady||this.client.isOpen)){this.isConnected=true;}}_prefixKey(key){return`${this.keyPrefix}${key}`;}async runScript(scriptName,keys=[],args=[]){const script=this.scripts[scriptName];if(!script){throw new Error(`Lua script "${scriptName}" not found`);}if(!this.client){throw new RedisConnectionError('Redis client is not configured or available. Please install "ioredis" or "redis", or provide an active client instance via the "client" option.');}const prefixedKeys=keys.map(k=>this._prefixKey(k));const stringArgs=args.map(a=>String(a));try{return await this.adapter.evalScript(script,prefixedKeys,stringArgs);}catch(err){throw new RedisConnectionError(`Redis script execution error: ${err.message}`,err);}}async get(key){try{const fullKey=this._prefixKey(key);let res;if(typeof this.client?.get==='function'){res=await this.client.get(fullKey);}else if(typeof this.client?.sendCommand==='function'){res=await this.adapter.sendCommand(['GET',fullKey]);}if(!res)return null;try{return JSON.parse(res);}catch{return res;}}catch(err){throw new RedisConnectionError(`Redis get error: ${err.message}`,err);}}async set(key,value,ttlMs){try{const fullKey=this._prefixKey(key);const stringValue=typeof value==='object'?JSON.stringify(value):String(value);await this.adapter.setWithTtl(fullKey,stringValue,ttlMs);return true;}catch(err){throw new RedisConnectionError(`Redis set error: ${err.message}`,err);}}async delete(key){try{const fullKey=this._prefixKey(key);return this.adapter.del(fullKey);}catch(err){throw new RedisConnectionError(`Redis del error: ${err.message}`,err);}}async clear(){try{const pattern=`${this.keyPrefix}*`;const keys=await this.adapter.scanKeys(pattern);if(keys&&keys.length>0){await this.adapter.delMany(keys);}}catch(err){throw new RedisConnectionError(`Redis clear error: ${err.message}`,err);}}async close(){if(this.isInternalClient&&this.client){if(typeof this.client.quit==='function'){await this.client.quit();}else if(typeof this.client.disconnect==='function'){this.client.disconnect();}}}}export default RedisStore;
|