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,7 +1,7 @@
1
1
  {
2
2
  "name": "mktcms",
3
3
  "configKey": "mktcms",
4
- "version": "0.3.8",
4
+ "version": "0.3.9",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.2",
7
7
  "unbuild": "3.6.1"
@@ -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
- function getState(clientId) {
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
- const state = getState(clientId);
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
- const state = getState(clientId);
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
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mktcms",
3
- "version": "0.3.8",
3
+ "version": "0.3.9",
4
4
  "description": "Simple CMS module for Nuxt",
5
5
  "repository": "mktcode/mktcms",
6
6
  "license": "MIT",