mktcms 0.3.8 → 0.3.9
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/module.json
CHANGED
|
@@ -1,14 +1,58 @@
|
|
|
1
1
|
import { createError, getRequestIP } from "h3";
|
|
2
2
|
import { useRuntimeConfig } from "nitropack/runtime";
|
|
3
3
|
const attempts = /* @__PURE__ */ new Map();
|
|
4
|
-
|
|
4
|
+
const SWEEP_EVERY_ACCESSES = 64;
|
|
5
|
+
const INACTIVE_GRACE_MS = 60 * 60 * 1e3;
|
|
6
|
+
const MAX_TRACKED_CLIENTS = 1e4;
|
|
7
|
+
let accessCount = 0;
|
|
8
|
+
function isInactiveState(state, now) {
|
|
9
|
+
return state.blockedUntil <= now && now - state.lastSeenAt > INACTIVE_GRACE_MS;
|
|
10
|
+
}
|
|
11
|
+
function evictInactiveClients(now) {
|
|
12
|
+
const toDelete = [];
|
|
13
|
+
attempts.forEach((state, clientId) => {
|
|
14
|
+
if (isInactiveState(state, now)) {
|
|
15
|
+
toDelete.push(clientId);
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
for (const clientId of toDelete) {
|
|
19
|
+
attempts.delete(clientId);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function enforceTrackedClientsCap() {
|
|
23
|
+
while (attempts.size > MAX_TRACKED_CLIENTS) {
|
|
24
|
+
let oldestClientId;
|
|
25
|
+
attempts.forEach((_state, clientId) => {
|
|
26
|
+
if (!oldestClientId) {
|
|
27
|
+
oldestClientId = clientId;
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
if (!oldestClientId) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
attempts.delete(oldestClientId);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function maybeSweep(now) {
|
|
37
|
+
accessCount += 1;
|
|
38
|
+
if (accessCount % SWEEP_EVERY_ACCESSES !== 0) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
evictInactiveClients(now);
|
|
42
|
+
enforceTrackedClientsCap();
|
|
43
|
+
}
|
|
44
|
+
function getState(clientId, now) {
|
|
5
45
|
const state = attempts.get(clientId);
|
|
6
46
|
if (state) {
|
|
47
|
+
state.lastSeenAt = now;
|
|
48
|
+
attempts.delete(clientId);
|
|
49
|
+
attempts.set(clientId, state);
|
|
7
50
|
return state;
|
|
8
51
|
}
|
|
9
52
|
const next = {
|
|
10
53
|
failedAt: [],
|
|
11
|
-
blockedUntil: 0
|
|
54
|
+
blockedUntil: 0,
|
|
55
|
+
lastSeenAt: now
|
|
12
56
|
};
|
|
13
57
|
attempts.set(clientId, next);
|
|
14
58
|
return next;
|
|
@@ -41,7 +85,8 @@ export function assertLoginNotRateLimited(event) {
|
|
|
41
85
|
const { windowMs } = getRateLimitSettings(event);
|
|
42
86
|
const clientId = getClientId(event);
|
|
43
87
|
const now = Date.now();
|
|
44
|
-
|
|
88
|
+
maybeSweep(now);
|
|
89
|
+
const state = getState(clientId, now);
|
|
45
90
|
clearStaleAttempts(state, now, windowMs);
|
|
46
91
|
if (state.blockedUntil > now) {
|
|
47
92
|
const retryAfterSeconds = Math.ceil((state.blockedUntil - now) / 1e3);
|
|
@@ -58,7 +103,8 @@ export function recordFailedLoginAttempt(event) {
|
|
|
58
103
|
const { maxAttempts, windowMs, blockMs } = getRateLimitSettings(event);
|
|
59
104
|
const clientId = getClientId(event);
|
|
60
105
|
const now = Date.now();
|
|
61
|
-
|
|
106
|
+
maybeSweep(now);
|
|
107
|
+
const state = getState(clientId, now);
|
|
62
108
|
clearStaleAttempts(state, now, windowMs);
|
|
63
109
|
state.failedAt.push(now);
|
|
64
110
|
if (state.failedAt.length >= maxAttempts) {
|
|
@@ -67,6 +113,8 @@ export function recordFailedLoginAttempt(event) {
|
|
|
67
113
|
}
|
|
68
114
|
}
|
|
69
115
|
export function clearFailedLoginAttempts(event) {
|
|
116
|
+
const now = Date.now();
|
|
117
|
+
maybeSweep(now);
|
|
70
118
|
const clientId = getClientId(event);
|
|
71
119
|
attempts.delete(clientId);
|
|
72
120
|
}
|