@hyperwatch/hyperwatch 4.2.0 → 4.3.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 +1 -1
- package/config/apache_syslog_example.js +29 -0
- package/config/default.js +1 -1
- package/config/example.js +1 -1
- package/config/express_websocket_example.js +28 -0
- package/package.json +38 -33
- package/scripts/fetch-cloudflare-ips.js +27 -0
- package/scripts/fetch-cloudfront-ips.js +29 -0
- package/scripts/fetch-openai-ips.js +34 -0
- package/src/app/api.js +21 -6
- package/src/app/websocket.js +28 -2
- package/src/constants.js +9 -0
- package/src/data/amazon-searchbot-ips.json +514 -0
- package/src/data/amazon-user-ips.json +1025 -0
- package/src/data/amazonbot-ips.json +520 -0
- package/src/data/chatgpt-user-ips.json +228 -0
- package/src/data/cloudflare-ips.json +24 -0
- package/src/data/cloudfront-ips.json +231 -0
- package/src/data/gptbot-ips.json +23 -0
- package/src/data/openai-searchbot-ips.json +37 -0
- package/src/index.js +24 -3
- package/src/input/websocket.js +33 -17
- package/src/lib/aggregator.js +68 -12
- package/src/lib/index.js +2 -0
- package/src/lib/persistence.js +72 -0
- package/src/lib/speed.js +43 -3
- package/src/lib/util.js +17 -1
- package/src/modules/agent.js +1 -1
- package/src/modules/history.js +86 -0
- package/src/modules/identity.js +101 -22
- package/src/modules/index.js +2 -0
- package/src/modules/language.js +1 -1
- package/src/modules/signature.js +15 -4
- package/src/modules/status.js +7 -7
- package/src/plugins/proxy.js +10 -203
- package/src/script.js +0 -1
- package/scripts/cloudfront-ips.js +0 -56
package/src/lib/aggregator.js
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
|
-
const { Map, Set, is } = require('immutable');
|
|
1
|
+
const { Map, Set, fromJS, is } = require('immutable');
|
|
2
2
|
|
|
3
3
|
const { Formatter, address, identity } = require('../lib/formatter');
|
|
4
4
|
const { Speed } = require('../lib/speed');
|
|
5
|
-
const {
|
|
5
|
+
const {
|
|
6
|
+
aggregateCount,
|
|
7
|
+
aggregateSum,
|
|
8
|
+
formatDuration,
|
|
9
|
+
md5,
|
|
10
|
+
} = require('../lib/util');
|
|
6
11
|
|
|
7
12
|
const defaultFormatter = new Formatter();
|
|
8
13
|
defaultFormatter.setFormats([
|
|
@@ -10,8 +15,11 @@ defaultFormatter.setFormats([
|
|
|
10
15
|
|
|
11
16
|
['address', address],
|
|
12
17
|
|
|
13
|
-
['
|
|
14
|
-
['
|
|
18
|
+
['count15m', (entry) => aggregateCount(entry, 'per_minute')],
|
|
19
|
+
['count24h', (entry) => aggregateCount(entry, 'per_hour')],
|
|
20
|
+
|
|
21
|
+
['execTime15m', (entry) => formatDuration(aggregateSum(entry, 'per_minute'))],
|
|
22
|
+
['execTime24h', (entry) => formatDuration(aggregateSum(entry, 'per_hour'))],
|
|
15
23
|
]);
|
|
16
24
|
|
|
17
25
|
const defaultEnricher = (entry, log) => {
|
|
@@ -42,9 +50,11 @@ const defaultIdentifier = (log) => {
|
|
|
42
50
|
};
|
|
43
51
|
|
|
44
52
|
const defaultSorters = {
|
|
45
|
-
|
|
46
|
-
|
|
53
|
+
count15m: (entry) => aggregateCount(entry, 'per_minute'),
|
|
54
|
+
count24h: (entry) => aggregateCount(entry, 'per_hour'),
|
|
47
55
|
latest: (entry) => entry.getIn(['speed', 'per_minute']).latest,
|
|
56
|
+
execTime15m: (entry) => aggregateSum(entry, 'per_minute'),
|
|
57
|
+
execTime24h: (entry) => aggregateSum(entry, 'per_hour'),
|
|
48
58
|
};
|
|
49
59
|
|
|
50
60
|
class Aggregator {
|
|
@@ -56,7 +66,7 @@ class Aggregator {
|
|
|
56
66
|
this.sorters = defaultSorters;
|
|
57
67
|
this.gcSize = 1000;
|
|
58
68
|
|
|
59
|
-
setInterval(() => this.gc(), 60 * 1000);
|
|
69
|
+
setInterval(() => this.gc(), 60 * 1000).unref();
|
|
60
70
|
}
|
|
61
71
|
|
|
62
72
|
setFormatter(fn) {
|
|
@@ -80,17 +90,29 @@ class Aggregator {
|
|
|
80
90
|
processLog(log) {
|
|
81
91
|
const identifier = this.identifier(log);
|
|
82
92
|
const id = md5(identifier);
|
|
93
|
+
const rawExecTime = Number(log.get('executionTime'));
|
|
94
|
+
const executionTime = Number.isFinite(rawExecTime) ? rawExecTime : 0;
|
|
83
95
|
|
|
84
96
|
if (!this.entries.has(id)) {
|
|
85
97
|
this.entries = this.entries
|
|
86
98
|
.setIn([id, 'id'], id)
|
|
87
99
|
.setIn([id, 'identifier'], identifier)
|
|
88
|
-
.setIn(
|
|
89
|
-
|
|
100
|
+
.setIn(
|
|
101
|
+
[id, 'speed', 'per_minute'],
|
|
102
|
+
new Speed(60, 15).hit(undefined, executionTime)
|
|
103
|
+
)
|
|
104
|
+
.setIn(
|
|
105
|
+
[id, 'speed', 'per_hour'],
|
|
106
|
+
new Speed(3600, 24).hit(undefined, executionTime)
|
|
107
|
+
);
|
|
90
108
|
} else {
|
|
91
109
|
this.entries = this.entries
|
|
92
|
-
.updateIn([id, 'speed', 'per_minute'], (speed) =>
|
|
93
|
-
|
|
110
|
+
.updateIn([id, 'speed', 'per_minute'], (speed) =>
|
|
111
|
+
speed.hit(undefined, executionTime)
|
|
112
|
+
)
|
|
113
|
+
.updateIn([id, 'speed', 'per_hour'], (speed) =>
|
|
114
|
+
speed.hit(undefined, executionTime)
|
|
115
|
+
);
|
|
94
116
|
}
|
|
95
117
|
|
|
96
118
|
this.entries = this.entries.updateIn([id], (entry) =>
|
|
@@ -106,7 +128,7 @@ class Aggregator {
|
|
|
106
128
|
|
|
107
129
|
getData({ raw, sort, format, limit }) {
|
|
108
130
|
if (!sort || !this.sorters[sort]) {
|
|
109
|
-
sort = '
|
|
131
|
+
sort = 'count15m';
|
|
110
132
|
}
|
|
111
133
|
|
|
112
134
|
const rawData = this.entries
|
|
@@ -142,6 +164,40 @@ class Aggregator {
|
|
|
142
164
|
|
|
143
165
|
this.entries = this.entries.filter((value, key) => keepList.has(key));
|
|
144
166
|
}
|
|
167
|
+
|
|
168
|
+
dump() {
|
|
169
|
+
return this.entries
|
|
170
|
+
.map((entry) => {
|
|
171
|
+
const plain = entry.toJS();
|
|
172
|
+
plain.speed = {
|
|
173
|
+
per_minute: entry.getIn(['speed', 'per_minute']).toJSON(),
|
|
174
|
+
per_hour: entry.getIn(['speed', 'per_hour']).toJSON(),
|
|
175
|
+
};
|
|
176
|
+
return plain;
|
|
177
|
+
})
|
|
178
|
+
.valueSeq()
|
|
179
|
+
.toArray();
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
load(data) {
|
|
183
|
+
for (const item of data) {
|
|
184
|
+
const { speed, ...rest } = item;
|
|
185
|
+
// fromJS deep-converts everything to Immutable structures.
|
|
186
|
+
// Signature headers must stay as a plain object (used with Object.entries),
|
|
187
|
+
// and addresses must be a Set, not a List — fix both after conversion.
|
|
188
|
+
let entry = fromJS(rest);
|
|
189
|
+
if (entry.hasIn(['signature', 'headers'])) {
|
|
190
|
+
entry = entry.setIn(['signature', 'headers'], rest.signature.headers);
|
|
191
|
+
}
|
|
192
|
+
if (entry.has('addresses')) {
|
|
193
|
+
entry = entry.update('addresses', (list) => Set(list));
|
|
194
|
+
}
|
|
195
|
+
entry = entry
|
|
196
|
+
.setIn(['speed', 'per_minute'], Speed.fromJSON(speed.per_minute))
|
|
197
|
+
.setIn(['speed', 'per_hour'], Speed.fromJSON(speed.per_hour));
|
|
198
|
+
this.entries = this.entries.set(rest.id, entry);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
145
201
|
}
|
|
146
202
|
|
|
147
203
|
module.exports = { Aggregator, defaultFormatter, defaultEnricher };
|
package/src/lib/index.js
CHANGED
|
@@ -2,6 +2,7 @@ const aggregator = require('./aggregator');
|
|
|
2
2
|
const cache = require('./cache');
|
|
3
3
|
const formatter = require('./formatter');
|
|
4
4
|
const logger = require('./logger');
|
|
5
|
+
const persistence = require('./persistence');
|
|
5
6
|
const pipeline = require('./pipeline');
|
|
6
7
|
const util = require('./util');
|
|
7
8
|
|
|
@@ -10,6 +11,7 @@ module.exports = {
|
|
|
10
11
|
cache,
|
|
11
12
|
formatter,
|
|
12
13
|
logger,
|
|
14
|
+
persistence,
|
|
13
15
|
pipeline,
|
|
14
16
|
util,
|
|
15
17
|
};
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
const debug = require('debug');
|
|
5
|
+
|
|
6
|
+
const debugPersistence = debug('hyperwatch:persistence');
|
|
7
|
+
|
|
8
|
+
const aggregators = Object.create(null);
|
|
9
|
+
|
|
10
|
+
const SAFE_NAME = /^[A-Za-z0-9._-]+$/;
|
|
11
|
+
|
|
12
|
+
function register(name, aggregator) {
|
|
13
|
+
if (!SAFE_NAME.test(name)) {
|
|
14
|
+
throw new Error(`Invalid aggregator name for persistence: "${name}"`);
|
|
15
|
+
}
|
|
16
|
+
aggregators[name] = aggregator;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function dump(dir) {
|
|
20
|
+
if (!fs.existsSync(dir)) {
|
|
21
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
22
|
+
}
|
|
23
|
+
for (const [name, aggregator] of Object.entries(aggregators)) {
|
|
24
|
+
const data = aggregator.dump();
|
|
25
|
+
const target = path.join(dir, `${name}.json`);
|
|
26
|
+
const tmp = path.join(dir, `${name}.json.tmp`);
|
|
27
|
+
fs.writeFileSync(tmp, JSON.stringify(data));
|
|
28
|
+
fs.renameSync(tmp, target);
|
|
29
|
+
}
|
|
30
|
+
debugPersistence(
|
|
31
|
+
`Dumped ${Object.keys(aggregators).length} aggregator(s) to ${dir}`
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function load(dir) {
|
|
36
|
+
if (!fs.existsSync(dir)) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
let files;
|
|
40
|
+
try {
|
|
41
|
+
files = fs.readdirSync(dir).filter((f) => f.endsWith('.json'));
|
|
42
|
+
} catch (err) {
|
|
43
|
+
debugPersistence(
|
|
44
|
+
`Cannot read persistence directory ${dir}: ${err.message}`
|
|
45
|
+
);
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
let loaded = 0;
|
|
49
|
+
for (const file of files) {
|
|
50
|
+
const name = path.basename(file, '.json');
|
|
51
|
+
if (aggregators[name]) {
|
|
52
|
+
try {
|
|
53
|
+
const data = JSON.parse(fs.readFileSync(path.join(dir, file), 'utf8'));
|
|
54
|
+
if (!Array.isArray(data)) {
|
|
55
|
+
debugPersistence(
|
|
56
|
+
`Skipping ${file}: expected array, got ${typeof data}`
|
|
57
|
+
);
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
aggregators[name].load(data);
|
|
61
|
+
loaded += data.length;
|
|
62
|
+
} catch (err) {
|
|
63
|
+
debugPersistence(`Skipping ${file}: ${err.message}`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
if (loaded) {
|
|
68
|
+
debugPersistence(`Loaded ${loaded} entries from ${dir}`);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
module.exports = { register, dump, load };
|
package/src/lib/speed.js
CHANGED
|
@@ -10,6 +10,7 @@ class Speed {
|
|
|
10
10
|
this.windowSize = windowSize;
|
|
11
11
|
this.size = size;
|
|
12
12
|
this.counters = Map();
|
|
13
|
+
this.sums = Map();
|
|
13
14
|
this.started = null;
|
|
14
15
|
this.latest = null;
|
|
15
16
|
}
|
|
@@ -18,23 +19,26 @@ class Speed {
|
|
|
18
19
|
gc() {
|
|
19
20
|
const cutoff = now() - this.size * this.windowSize;
|
|
20
21
|
this.counters = this.counters.filter((c, t) => parseInt(t) > cutoff);
|
|
22
|
+
this.sums = this.sums.filter((c, t) => parseInt(t) > cutoff);
|
|
21
23
|
}
|
|
22
24
|
|
|
23
|
-
hit(time = now()) {
|
|
25
|
+
hit(time = now(), value) {
|
|
24
26
|
this.started = !this.started ? time : Math.min(this.started, time);
|
|
25
27
|
this.latest = !this.latest ? time : Math.max(this.latest, time);
|
|
26
28
|
const idx = time - (time % this.windowSize);
|
|
27
29
|
this.counters = this.counters.update(`${idx}`, 0, (n) => n + 1);
|
|
30
|
+
if (value !== undefined) {
|
|
31
|
+
this.sums = this.sums.update(`${idx}`, 0, (n) => n + value);
|
|
32
|
+
}
|
|
28
33
|
this.gc();
|
|
29
34
|
return this;
|
|
30
35
|
}
|
|
31
36
|
|
|
32
|
-
compute() {
|
|
37
|
+
compute(time = now()) {
|
|
33
38
|
this.gc();
|
|
34
39
|
if (!this.started) {
|
|
35
40
|
return List();
|
|
36
41
|
}
|
|
37
|
-
const time = now();
|
|
38
42
|
return Range(0, this.size)
|
|
39
43
|
.map((n) => {
|
|
40
44
|
let t = time - n * this.windowSize;
|
|
@@ -45,6 +49,42 @@ class Speed {
|
|
|
45
49
|
})
|
|
46
50
|
.filter((v) => v !== undefined);
|
|
47
51
|
}
|
|
52
|
+
|
|
53
|
+
computeSum(time = now()) {
|
|
54
|
+
this.gc();
|
|
55
|
+
if (!this.started) {
|
|
56
|
+
return List();
|
|
57
|
+
}
|
|
58
|
+
return Range(0, this.size)
|
|
59
|
+
.map((n) => {
|
|
60
|
+
let t = time - n * this.windowSize;
|
|
61
|
+
t = t - (t % this.windowSize);
|
|
62
|
+
if (t >= this.started - (this.started % this.windowSize)) {
|
|
63
|
+
return this.sums.get(`${t}`, 0);
|
|
64
|
+
}
|
|
65
|
+
})
|
|
66
|
+
.filter((v) => v !== undefined);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
toJSON() {
|
|
70
|
+
return {
|
|
71
|
+
windowSize: this.windowSize,
|
|
72
|
+
size: this.size,
|
|
73
|
+
counters: this.counters.toObject(),
|
|
74
|
+
sums: this.sums.toObject(),
|
|
75
|
+
started: this.started,
|
|
76
|
+
latest: this.latest,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
static fromJSON(data) {
|
|
81
|
+
const speed = new Speed(data.windowSize, data.size);
|
|
82
|
+
speed.counters = Map(data.counters);
|
|
83
|
+
speed.sums = Map(data.sums);
|
|
84
|
+
speed.started = data.started;
|
|
85
|
+
speed.latest = data.latest;
|
|
86
|
+
return speed;
|
|
87
|
+
}
|
|
48
88
|
}
|
|
49
89
|
|
|
50
90
|
module.exports = {
|
package/src/lib/util.js
CHANGED
|
@@ -34,12 +34,28 @@ exports.createLog = (req, res) => {
|
|
|
34
34
|
});
|
|
35
35
|
};
|
|
36
36
|
|
|
37
|
-
exports.
|
|
37
|
+
exports.aggregateCount = (entry, key) =>
|
|
38
38
|
entry
|
|
39
39
|
.getIn(['speed', key])
|
|
40
40
|
.compute()
|
|
41
41
|
.reduce((p, c) => p + c, 0);
|
|
42
42
|
|
|
43
|
+
exports.aggregateSum = (entry, key) =>
|
|
44
|
+
entry
|
|
45
|
+
.getIn(['speed', key])
|
|
46
|
+
.computeSum()
|
|
47
|
+
.reduce((p, c) => p + c, 0);
|
|
48
|
+
|
|
49
|
+
exports.formatDuration = (ms) => {
|
|
50
|
+
const totalSeconds = ms / 1000;
|
|
51
|
+
const minutes = Math.floor(totalSeconds / 60);
|
|
52
|
+
const seconds = Math.round(totalSeconds % 60);
|
|
53
|
+
if (minutes > 0) {
|
|
54
|
+
return seconds > 0 ? `${minutes}m${seconds}s` : `${minutes}m`;
|
|
55
|
+
}
|
|
56
|
+
return `${totalSeconds.toFixed(1)}s`;
|
|
57
|
+
};
|
|
58
|
+
|
|
43
59
|
exports.formatTable = (data) => {
|
|
44
60
|
if (!data || data.length === 0) {
|
|
45
61
|
return '';
|
package/src/modules/agent.js
CHANGED
|
@@ -70,7 +70,7 @@ function init() {
|
|
|
70
70
|
pipeline.getNode('main').map(augment).registerNode('main');
|
|
71
71
|
|
|
72
72
|
aggregator.defaultFormatter.insertFormat('agent', agentFormat, {
|
|
73
|
-
before: '
|
|
73
|
+
before: 'count15m',
|
|
74
74
|
color: 'grey',
|
|
75
75
|
});
|
|
76
76
|
aggregator.defaultFormatter.insertFormat('os', osFormat, {
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
const { api } = require('../app');
|
|
2
|
+
const constants = require('../constants');
|
|
3
|
+
const pipeline = require('../lib/pipeline');
|
|
4
|
+
|
|
5
|
+
class CircularBuffer {
|
|
6
|
+
constructor(capacity = 1000) {
|
|
7
|
+
this.capacity = capacity;
|
|
8
|
+
this.buffer = new Array(capacity);
|
|
9
|
+
this.pointer = 0;
|
|
10
|
+
this.size = 0;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
push(item) {
|
|
14
|
+
this.buffer[this.pointer] = item;
|
|
15
|
+
this.pointer = (this.pointer + 1) % this.capacity;
|
|
16
|
+
if (this.size < this.capacity) {
|
|
17
|
+
this.size++;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
toArray() {
|
|
22
|
+
if (this.size < this.capacity) {
|
|
23
|
+
return this.buffer.slice(0, this.size).reverse();
|
|
24
|
+
}
|
|
25
|
+
return [
|
|
26
|
+
...this.buffer.slice(this.pointer),
|
|
27
|
+
...this.buffer.slice(0, this.pointer),
|
|
28
|
+
].reverse();
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function start() {
|
|
33
|
+
const capacity =
|
|
34
|
+
(constants.modules.history && constants.modules.history.capacity) || 1000;
|
|
35
|
+
const buffers = {};
|
|
36
|
+
|
|
37
|
+
function registerNodeHistory(name, node) {
|
|
38
|
+
const buffer = new CircularBuffer(capacity);
|
|
39
|
+
buffers[name] = buffer;
|
|
40
|
+
|
|
41
|
+
node.map((log) => {
|
|
42
|
+
buffer.push(log);
|
|
43
|
+
return log;
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
api.get(`/history/${name}.json`, (req, res) => {
|
|
47
|
+
const { identity, signature, address } = req.query;
|
|
48
|
+
const limit = parseInt(req.query.limit, 10) || 100;
|
|
49
|
+
|
|
50
|
+
let logs = buffer.toArray();
|
|
51
|
+
|
|
52
|
+
if (identity) {
|
|
53
|
+
logs = logs.filter((log) => log.get('identity') === identity);
|
|
54
|
+
}
|
|
55
|
+
if (signature) {
|
|
56
|
+
logs = logs.filter(
|
|
57
|
+
(log) => log.getIn(['signature', 'id']) === signature
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
if (address) {
|
|
61
|
+
logs = logs.filter(
|
|
62
|
+
(log) => log.getIn(['address', 'value']) === address
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
logs = logs.slice(0, limit);
|
|
67
|
+
|
|
68
|
+
res.json(logs);
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
for (const [name, node] of Object.entries(pipeline.nodes)) {
|
|
73
|
+
registerNodeHistory(name, node);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Auto-register future nodes
|
|
77
|
+
const originalRegisterNode = pipeline.registerNode.bind(pipeline);
|
|
78
|
+
pipeline.registerNode = function (name, node) {
|
|
79
|
+
originalRegisterNode(name, node);
|
|
80
|
+
if (!buffers[name]) {
|
|
81
|
+
registerNodeHistory(name, node);
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
module.exports = { start };
|
package/src/modules/identity.js
CHANGED
|
@@ -1,6 +1,14 @@
|
|
|
1
|
-
const IPCIDR = require('ip-cidr');
|
|
1
|
+
const IPCIDR = require('ip-cidr').default;
|
|
2
2
|
|
|
3
3
|
const api = require('../app/api');
|
|
4
|
+
// Bot IP lists for identity verification
|
|
5
|
+
// Run `node scripts/fetch-openai-ips.js` to update OpenAI lists
|
|
6
|
+
const amazonSearchBotIps = require('../data/amazon-searchbot-ips.json');
|
|
7
|
+
const amazonUserIps = require('../data/amazon-user-ips.json');
|
|
8
|
+
const amazonBotIps = require('../data/amazonbot-ips.json');
|
|
9
|
+
const chatgptUserIps = require('../data/chatgpt-user-ips.json');
|
|
10
|
+
const gptbotIps = require('../data/gptbot-ips.json');
|
|
11
|
+
const openaiSearchbotIps = require('../data/openai-searchbot-ips.json');
|
|
4
12
|
const { Aggregator } = require('../lib/aggregator');
|
|
5
13
|
const pipeline = require('../lib/pipeline');
|
|
6
14
|
|
|
@@ -69,6 +77,7 @@ function augment(log) {
|
|
|
69
77
|
return hostname && hostname.endsWith('.mojeek.com')
|
|
70
78
|
? log.set('identity', 'Mojeek')
|
|
71
79
|
: log;
|
|
80
|
+
case 'AwarioBot':
|
|
72
81
|
case 'BLEXBot':
|
|
73
82
|
return hostname && hostname.endsWith('.webmeup.com')
|
|
74
83
|
? log.set('identity', 'WebMeUp')
|
|
@@ -89,13 +98,11 @@ function augment(log) {
|
|
|
89
98
|
return hostname && hostname.endsWith('.naver.com')
|
|
90
99
|
? log.set('identity', 'Naver')
|
|
91
100
|
: log;
|
|
92
|
-
|
|
93
|
-
return hostname && hostname.endsWith('.fbsv.net')
|
|
94
|
-
? log.set('identity', 'Facebook')
|
|
95
|
-
: log;
|
|
101
|
+
|
|
96
102
|
case 'Stripe':
|
|
103
|
+
case 'Stripebot':
|
|
97
104
|
return hostname && hostname.endsWith('.stripe.com')
|
|
98
|
-
? log.set('identity',
|
|
105
|
+
? log.set('identity', 'Stripe')
|
|
99
106
|
: log;
|
|
100
107
|
case 'UptimeRobot':
|
|
101
108
|
return hostname && hostname.endsWith('.uptimerobot.com')
|
|
@@ -127,9 +134,17 @@ function augment(log) {
|
|
|
127
134
|
? log.set('identity', 'Sirportly')
|
|
128
135
|
: log;
|
|
129
136
|
case 'Bytespider':
|
|
130
|
-
return hostname &&
|
|
137
|
+
return hostname &&
|
|
138
|
+
(hostname.endsWith('.crawl.bytedance.com') ||
|
|
139
|
+
hostname.endsWith('.ap-southeast-1.compute.amazonaws.com'))
|
|
131
140
|
? log.set('identity', family)
|
|
132
141
|
: log;
|
|
142
|
+
case 'TikTokSpider':
|
|
143
|
+
return hostname &&
|
|
144
|
+
(hostname.endsWith('.crawl.bytedance.com') ||
|
|
145
|
+
hostname.endsWith('.ap-southeast-1.compute.amazonaws.com'))
|
|
146
|
+
? log.set('identity', 'TikTok')
|
|
147
|
+
: log;
|
|
133
148
|
case 'Mail.RU Bot':
|
|
134
149
|
case 'Mail.RU Bot Img':
|
|
135
150
|
case 'Mail.RU Bot Fast':
|
|
@@ -182,6 +197,14 @@ function augment(log) {
|
|
|
182
197
|
return hostname && hostname.endsWith('.neevabot.com')
|
|
183
198
|
? log.set('identity', 'Neevabot')
|
|
184
199
|
: log;
|
|
200
|
+
case 'RootCrawl-Crawler':
|
|
201
|
+
return hostname && hostname.endsWith('.rootcrawl.org')
|
|
202
|
+
? log.set('identity', 'RootCrawl')
|
|
203
|
+
: log;
|
|
204
|
+
case 'bl.uk ldfc bot':
|
|
205
|
+
return hostname && hostname.endsWith('.bl.uk')
|
|
206
|
+
? log.set('identity', 'British Library')
|
|
207
|
+
: log;
|
|
185
208
|
case 'DataForSeoBot':
|
|
186
209
|
return hostname && hostname.endsWith('.dataforseo.com')
|
|
187
210
|
? log.set('identity', 'DataForSeo')
|
|
@@ -196,9 +219,24 @@ function augment(log) {
|
|
|
196
219
|
? log.set('identity', 'InfoTiger')
|
|
197
220
|
: log;
|
|
198
221
|
case 'Amazonbot':
|
|
199
|
-
return hostname && hostname.endsWith('.crawl.amazonbot.amazon')
|
|
222
|
+
return (hostname && hostname.endsWith('.crawl.amazonbot.amazon')) ||
|
|
223
|
+
amazonBotIps.some((cidr) => new IPCIDR(cidr).contains(address))
|
|
200
224
|
? log.set('identity', 'Amazonbot')
|
|
201
225
|
: log;
|
|
226
|
+
case 'Amzn-SearchBot':
|
|
227
|
+
return (hostname && hostname.endsWith('.crawl.amazonbot.amazon')) ||
|
|
228
|
+
amazonSearchBotIps.some((cidr) => new IPCIDR(cidr).contains(address))
|
|
229
|
+
? log.set('identity', 'Amazon SearchBot')
|
|
230
|
+
: log;
|
|
231
|
+
case 'Amzn-User':
|
|
232
|
+
return (hostname && hostname.endsWith('.crawl.amazonbot.amazon')) ||
|
|
233
|
+
amazonUserIps.some((cidr) => new IPCIDR(cidr).contains(address))
|
|
234
|
+
? log.set('identity', 'Amazon User')
|
|
235
|
+
: log;
|
|
236
|
+
case 'SERankingBacklinksBot':
|
|
237
|
+
return hostname && hostname.endsWith('.blex.seranking.com')
|
|
238
|
+
? log.set('identity', 'SE Ranking')
|
|
239
|
+
: log;
|
|
202
240
|
|
|
203
241
|
// Per hostname + CIDR
|
|
204
242
|
case 'Twitterbot':
|
|
@@ -211,6 +249,11 @@ function augment(log) {
|
|
|
211
249
|
(address && new IPCIDR('2a02:598::/32').contains(address))
|
|
212
250
|
? log.set('identity', 'Seznam')
|
|
213
251
|
: log;
|
|
252
|
+
case 'FacebookBot':
|
|
253
|
+
return (hostname && hostname.endsWith('.fbsv.net')) ||
|
|
254
|
+
(address && new IPCIDR('2a03:2880::/29').contains(address))
|
|
255
|
+
? log.set('identity', 'Facebook')
|
|
256
|
+
: log;
|
|
214
257
|
|
|
215
258
|
// Per CIDR
|
|
216
259
|
case 'github-camo':
|
|
@@ -221,6 +264,10 @@ function augment(log) {
|
|
|
221
264
|
return address && new IPCIDR('216.244.64.0/19').contains(address)
|
|
222
265
|
? log.set('identity', 'Moz')
|
|
223
266
|
: log;
|
|
267
|
+
case 'AliyunSecBot':
|
|
268
|
+
return address && new IPCIDR('8.217.0.0/16').contains(address)
|
|
269
|
+
? log.set('identity', family)
|
|
270
|
+
: log;
|
|
224
271
|
case '360Spider':
|
|
225
272
|
return address && new IPCIDR('42.236.10.0/24').contains(address)
|
|
226
273
|
? log.set('identity', family)
|
|
@@ -230,26 +277,26 @@ function augment(log) {
|
|
|
230
277
|
? log.set('identity', family)
|
|
231
278
|
: log;
|
|
232
279
|
case 'OAI-SearchBot':
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
new IPCIDR('172.203.190.128/28').contains(address) ||
|
|
237
|
-
new IPCIDR('51.8.102.0/24').contains(address))
|
|
280
|
+
return openaiSearchbotIps.some((cidr) =>
|
|
281
|
+
new IPCIDR(cidr).contains(address)
|
|
282
|
+
)
|
|
238
283
|
? log.set('identity', 'OpenAI SearchBot')
|
|
239
284
|
: log;
|
|
240
285
|
case 'GPTBot':
|
|
241
|
-
|
|
242
|
-
return address &&
|
|
243
|
-
(new IPCIDR('52.230.152.0/24').contains(address) ||
|
|
244
|
-
new IPCIDR('52.233.106.0/24').contains(address) ||
|
|
245
|
-
new IPCIDR('20.171.206.0/24').contains(address) ||
|
|
246
|
-
new IPCIDR('20.171.207.0/24').contains(address) ||
|
|
247
|
-
new IPCIDR('4.227.36.0/25').contains(address))
|
|
286
|
+
return gptbotIps.some((cidr) => new IPCIDR(cidr).contains(address))
|
|
248
287
|
? log.set('identity', 'OpenAI GPTBot')
|
|
249
288
|
: log;
|
|
289
|
+
case 'ChatGPT-User':
|
|
290
|
+
// https://openai.com/chatgpt-user.json
|
|
291
|
+
return chatgptUserIps.some((cidr) => new IPCIDR(cidr).contains(address))
|
|
292
|
+
? log.set('identity', 'ChatGPT')
|
|
293
|
+
: log;
|
|
250
294
|
case 'meta-externalagent':
|
|
251
|
-
|
|
252
|
-
|
|
295
|
+
case 'meta-webindexer':
|
|
296
|
+
return address &&
|
|
297
|
+
(new IPCIDR('2a03:2880::/29').contains(address) ||
|
|
298
|
+
new IPCIDR('2a06:98c0:3600::/48').contains(address))
|
|
299
|
+
? log.set('identity', 'Meta')
|
|
253
300
|
: log;
|
|
254
301
|
|
|
255
302
|
// EC2
|
|
@@ -292,12 +339,32 @@ function augment(log) {
|
|
|
292
339
|
return hostname && hostname.endsWith('.us-east-2.compute.amazonaws.com')
|
|
293
340
|
? log.set('identity', 'Claude')
|
|
294
341
|
: log;
|
|
342
|
+
case 'PerplexityBot':
|
|
343
|
+
return hostname && hostname.endsWith('.compute-1.amazonaws.com')
|
|
344
|
+
? log.set('identity', 'Perplexity')
|
|
345
|
+
: log;
|
|
295
346
|
|
|
296
347
|
// GCE
|
|
348
|
+
case 'Aranet-SearchBot':
|
|
349
|
+
return hostname && hostname.endsWith('.googleusercontent.com')
|
|
350
|
+
? log.set('identity', 'Aranet')
|
|
351
|
+
: log;
|
|
352
|
+
case 'Discordbot':
|
|
353
|
+
return hostname && hostname.endsWith('.googleusercontent.com')
|
|
354
|
+
? log.set('identity', 'Discord')
|
|
355
|
+
: log;
|
|
297
356
|
case 'VelenPublicWebCrawler':
|
|
298
357
|
return hostname && hostname.endsWith('.googleusercontent.com')
|
|
299
358
|
? log.set('identity', 'Velen')
|
|
300
359
|
: log;
|
|
360
|
+
case 'SleepBot':
|
|
361
|
+
return hostname && hostname.endsWith('.googleusercontent.com')
|
|
362
|
+
? log.set('identity', 'SleepBot')
|
|
363
|
+
: log;
|
|
364
|
+
case 'DolfeEngineCrawler':
|
|
365
|
+
return hostname && hostname.endsWith('.googleusercontent.com')
|
|
366
|
+
? log.set('identity', 'Dolfe')
|
|
367
|
+
: log;
|
|
301
368
|
|
|
302
369
|
// Hetzner
|
|
303
370
|
case 'Ubermetrics':
|
|
@@ -312,6 +379,18 @@ function augment(log) {
|
|
|
312
379
|
return hostname && hostname.endsWith('.clients.your-server.de')
|
|
313
380
|
? log.set('identity', 'MegaIndex.ru')
|
|
314
381
|
: log;
|
|
382
|
+
case 'ev-crawler':
|
|
383
|
+
return hostname && hostname.endsWith('.headline.com')
|
|
384
|
+
? log.set('identity', 'Headline')
|
|
385
|
+
: log;
|
|
386
|
+
case 'SentryUptimeBot':
|
|
387
|
+
return hostname && hostname.endsWith('.googleusercontent.com')
|
|
388
|
+
? log.set('identity', 'Sentry')
|
|
389
|
+
: log;
|
|
390
|
+
case 'FlipboardProxy':
|
|
391
|
+
return hostname && hostname.endsWith('.flipboard.com')
|
|
392
|
+
? log.set('identity', 'Flipboard')
|
|
393
|
+
: log;
|
|
315
394
|
}
|
|
316
395
|
|
|
317
396
|
// Hostname only
|
package/src/modules/index.js
CHANGED
package/src/modules/language.js
CHANGED