hookarmor 1.0.3 → 1.1.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 +303 -260
- package/bin/hookarmor.js +168 -133
- package/package.json +52 -52
- package/src/alerter.js +72 -63
- package/src/dispatcher.js +409 -356
- package/src/netguard.js +117 -0
- package/src/public/index.html +96 -40
- package/src/server.js +544 -393
- package/src/storage.js +337 -266
- package/src/worker.js +72 -62
package/src/worker.js
CHANGED
|
@@ -1,62 +1,72 @@
|
|
|
1
|
-
const EventEmitter = require('events');
|
|
2
|
-
|
|
3
|
-
class RetryWorker extends EventEmitter {
|
|
4
|
-
constructor(storage, dispatcher, options = {}) {
|
|
5
|
-
super();
|
|
6
|
-
this.storage = storage;
|
|
7
|
-
this.dispatcher = dispatcher;
|
|
8
|
-
this.intervalMs = options.intervalMs || 5000;
|
|
9
|
-
this.
|
|
10
|
-
this.
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
if (this.timer
|
|
18
|
-
this.
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
this.
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
|
|
1
|
+
const EventEmitter = require('events');
|
|
2
|
+
|
|
3
|
+
class RetryWorker extends EventEmitter {
|
|
4
|
+
constructor(storage, dispatcher, options = {}) {
|
|
5
|
+
super();
|
|
6
|
+
this.storage = storage;
|
|
7
|
+
this.dispatcher = dispatcher;
|
|
8
|
+
this.intervalMs = options.intervalMs || 5000;
|
|
9
|
+
this.retentionDays = options.retentionDays || 0;
|
|
10
|
+
this.pruneIntervalMs = options.pruneIntervalMs || 60 * 60 * 1000;
|
|
11
|
+
this.lastPruneAt = 0;
|
|
12
|
+
this.timer = null;
|
|
13
|
+
this.isProcessing = false;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
start() {
|
|
17
|
+
if (this.timer) return;
|
|
18
|
+
this.timer = setInterval(() => this.tick(), this.intervalMs);
|
|
19
|
+
// Unref timer so it doesn't hold Node process open during graceful shutdown or tests if desired
|
|
20
|
+
if (this.timer.unref) this.timer.unref();
|
|
21
|
+
this.emit('started');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
stop() {
|
|
25
|
+
if (this.timer) {
|
|
26
|
+
clearInterval(this.timer);
|
|
27
|
+
this.timer = null;
|
|
28
|
+
this.emit('stopped');
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
prune() {
|
|
33
|
+
if (!this.retentionDays || Date.now() - this.lastPruneAt < this.pruneIntervalMs) return;
|
|
34
|
+
this.lastPruneAt = Date.now();
|
|
35
|
+
const removed = this.storage.pruneEvents(this.retentionDays);
|
|
36
|
+
if (removed > 0) this.emit('pruned', { removed });
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async tick() {
|
|
40
|
+
if (this.isProcessing) return;
|
|
41
|
+
this.isProcessing = true;
|
|
42
|
+
|
|
43
|
+
try {
|
|
44
|
+
this.prune();
|
|
45
|
+
|
|
46
|
+
const eventsDue = this.storage.getEventsDueForRetry(25);
|
|
47
|
+
if (!eventsDue || eventsDue.length === 0) return;
|
|
48
|
+
|
|
49
|
+
for (const event of eventsDue) {
|
|
50
|
+
const claimed = this.storage.claimEventForRetry(event.id);
|
|
51
|
+
if (!claimed) continue; // Another process or manual replay already claimed it
|
|
52
|
+
|
|
53
|
+
const endpoint = this.storage.getEndpoint(event.endpoint_id);
|
|
54
|
+
if (!endpoint) continue;
|
|
55
|
+
|
|
56
|
+
this.emit('retry:claimed', { eventId: event.id, endpointId: endpoint.id });
|
|
57
|
+
|
|
58
|
+
// Dispatch asynchronously without blocking next iterations
|
|
59
|
+
this.dispatcher.dispatch(event, endpoint, { replay: true }).catch((err) => {
|
|
60
|
+
this.emit('retry:error', { eventId: event.id, error: err.message });
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
} catch (err) {
|
|
64
|
+
if (this.listenerCount('error') > 0) this.emit('error', err);
|
|
65
|
+
else console.error('[RetryWorker] Tick failed:', err.message);
|
|
66
|
+
} finally {
|
|
67
|
+
this.isProcessing = false;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
module.exports = RetryWorker;
|