node-red-contrib-ipgeolocation 1.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/LICENSE +21 -0
- package/README.md +423 -0
- package/examples/01 - Single IP Lookup.json +102 -0
- package/examples/02 - Bulk IP Lookup.json +98 -0
- package/examples/03 - IP Security Lookup.json +98 -0
- package/examples/04 - Bulk IP Security.json +96 -0
- package/examples/05 - Abuse Contact Lookup.json +98 -0
- package/examples/06 - ASN Lookup.json +102 -0
- package/examples/07 - Timezone Lookup.json +101 -0
- package/examples/08 - Astronomy (Sun & Moon).json +109 -0
- package/examples/09 - User-Agent Parser.json +96 -0
- package/index.js +22 -0
- package/nodes/ipgeo-abuse.html +203 -0
- package/nodes/ipgeo-abuse.js +148 -0
- package/nodes/ipgeo-asn.html +297 -0
- package/nodes/ipgeo-asn.js +197 -0
- package/nodes/ipgeo-astronomy.html +242 -0
- package/nodes/ipgeo-astronomy.js +195 -0
- package/nodes/ipgeo-bulk-security.html +166 -0
- package/nodes/ipgeo-bulk-security.js +177 -0
- package/nodes/ipgeo-bulk.html +158 -0
- package/nodes/ipgeo-bulk.js +146 -0
- package/nodes/ipgeo-config.html +72 -0
- package/nodes/ipgeo-config.js +30 -0
- package/nodes/ipgeo-lookup.html +300 -0
- package/nodes/ipgeo-lookup.js +165 -0
- package/nodes/ipgeo-security.html +214 -0
- package/nodes/ipgeo-security.js +150 -0
- package/nodes/ipgeo-timezone.html +201 -0
- package/nodes/ipgeo-timezone.js +154 -0
- package/nodes/ipgeo-useragent.html +133 -0
- package/nodes/ipgeo-useragent.js +110 -0
- package/nodes/lib/api-client.js +210 -0
- package/nodes/lib/node-helpers.js +214 -0
- package/package.json +72 -0
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/node-helpers.js
|
|
3
|
+
*
|
|
4
|
+
* Utility functions shared across all IPGeolocation.io nodes.
|
|
5
|
+
*
|
|
6
|
+
* Contents
|
|
7
|
+
* ─────────
|
|
8
|
+
* • setStatus / clearStatus — node status shorthands
|
|
9
|
+
* • resolveValue — reads msg / flow / global / str values
|
|
10
|
+
* • setOutputProperty — writes to any msg / flow / global path
|
|
11
|
+
* • handleNodeError — standard error handler (status + done)
|
|
12
|
+
* • buildCommonParams — assembles shared query params (apiKey, lang, output, fields, excludes)
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
'use strict';
|
|
16
|
+
|
|
17
|
+
// ─── Status Helpers ──────────────────────────────────────────────────────────
|
|
18
|
+
|
|
19
|
+
const STATUS = {
|
|
20
|
+
IDLE: { fill: 'grey', shape: 'ring', text: '' },
|
|
21
|
+
WORKING: { fill: 'blue', shape: 'dot', text: 'requesting…' },
|
|
22
|
+
OK: { fill: 'green', shape: 'dot', text: 'ok' },
|
|
23
|
+
ERROR: { fill: 'red', shape: 'ring', text: 'error' },
|
|
24
|
+
NO_KEY: { fill: 'red', shape: 'ring', text: 'no API key' },
|
|
25
|
+
RATE_LIM: { fill: 'yellow', shape: 'ring', text: 'rate limited' },
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Set a node status using a named key from STATUS, or a raw status object.
|
|
30
|
+
* @param {object} node
|
|
31
|
+
* @param {string|object} state Key from STATUS or raw { fill, shape, text }
|
|
32
|
+
* @param {string} [suffix] Optional text appended to STATUS[state].text
|
|
33
|
+
*/
|
|
34
|
+
function setStatus(node, state, suffix) {
|
|
35
|
+
if (typeof state === 'string' && STATUS[state]) {
|
|
36
|
+
const s = { ...STATUS[state] };
|
|
37
|
+
if (suffix) s.text = suffix;
|
|
38
|
+
node.status(s);
|
|
39
|
+
} else {
|
|
40
|
+
node.status(state);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Clear a node's status indicator. */
|
|
45
|
+
function clearStatus(node) {
|
|
46
|
+
node.status({});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// ─── Value Resolution ────────────────────────────────────────────────────────
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Resolve a typed value from the message context.
|
|
53
|
+
* Supports Node-RED's standard { value, type } descriptor types:
|
|
54
|
+
* msg, flow, global, str, num, bool, json, env
|
|
55
|
+
*
|
|
56
|
+
* @param {object} RED Node-RED runtime
|
|
57
|
+
* @param {object} node The node instance (for flow/global context access)
|
|
58
|
+
* @param {object} msg The current message
|
|
59
|
+
* @param {object} descriptor { value: string, type: string }
|
|
60
|
+
* @returns {*} Resolved value or undefined
|
|
61
|
+
*/
|
|
62
|
+
function resolveValue(RED, node, msg, descriptor) {
|
|
63
|
+
if (!descriptor) return undefined;
|
|
64
|
+
const { value, type } = descriptor;
|
|
65
|
+
|
|
66
|
+
switch (type) {
|
|
67
|
+
case 'msg':
|
|
68
|
+
return RED.util.getMessageProperty(msg, value);
|
|
69
|
+
|
|
70
|
+
case 'flow':
|
|
71
|
+
return node.context().flow.get(value);
|
|
72
|
+
|
|
73
|
+
case 'global':
|
|
74
|
+
return node.context().global.get(value);
|
|
75
|
+
|
|
76
|
+
case 'str':
|
|
77
|
+
return String(value);
|
|
78
|
+
|
|
79
|
+
case 'num':
|
|
80
|
+
return Number(value);
|
|
81
|
+
|
|
82
|
+
case 'bool':
|
|
83
|
+
return value === true || value === 'true';
|
|
84
|
+
|
|
85
|
+
case 'json':
|
|
86
|
+
try { return JSON.parse(value); } catch { return undefined; }
|
|
87
|
+
|
|
88
|
+
case 'env':
|
|
89
|
+
return process.env[value];
|
|
90
|
+
|
|
91
|
+
default:
|
|
92
|
+
return value;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// ─── Output Property Setter ──────────────────────────────────────────────────
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Write a value to a target on msg / flow / global.
|
|
100
|
+
*
|
|
101
|
+
* @param {object} RED
|
|
102
|
+
* @param {object} node
|
|
103
|
+
* @param {object} msg
|
|
104
|
+
* @param {object} descriptor { value: string, type: 'msg'|'flow'|'global' }
|
|
105
|
+
* @param {*} data Value to write
|
|
106
|
+
*/
|
|
107
|
+
function setOutputProperty(RED, node, msg, descriptor, data) {
|
|
108
|
+
if (!descriptor) return;
|
|
109
|
+
const { value, type } = descriptor;
|
|
110
|
+
|
|
111
|
+
switch (type) {
|
|
112
|
+
case 'msg':
|
|
113
|
+
RED.util.setMessageProperty(msg, value, data);
|
|
114
|
+
break;
|
|
115
|
+
case 'flow':
|
|
116
|
+
node.context().flow.set(value, data);
|
|
117
|
+
break;
|
|
118
|
+
case 'global':
|
|
119
|
+
node.context().global.set(value, data);
|
|
120
|
+
break;
|
|
121
|
+
default:
|
|
122
|
+
RED.util.setMessageProperty(msg, value, data);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// ─── Error Handler ───────────────────────────────────────────────────────────
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Standard error handler called from within input event listeners.
|
|
130
|
+
* Sets an appropriate status, logs to the Node-RED debug panel, and
|
|
131
|
+
* calls done(err) / node.error(err, msg) for Catch-node compatibility.
|
|
132
|
+
*
|
|
133
|
+
* @param {object} node
|
|
134
|
+
* @param {Error} err
|
|
135
|
+
* @param {object} msg
|
|
136
|
+
* @param {Function} done Node-RED 1.0+ done callback
|
|
137
|
+
* @param {Function} send Node-RED send function (used for error port if configured)
|
|
138
|
+
* @param {boolean} [twoOutputs] Whether the node uses a second (error) output
|
|
139
|
+
*/
|
|
140
|
+
function handleNodeError(node, err, msg, done, send, twoOutputs) {
|
|
141
|
+
const code = (err.code || 'ERROR').toUpperCase();
|
|
142
|
+
|
|
143
|
+
// Choose the most informative status text
|
|
144
|
+
let statusText = err.message || 'unknown error';
|
|
145
|
+
if (statusText.length > 50) statusText = statusText.substring(0, 47) + '…';
|
|
146
|
+
|
|
147
|
+
if (code === 'RATE_LIMITED') {
|
|
148
|
+
setStatus(node, 'RATE_LIM');
|
|
149
|
+
} else {
|
|
150
|
+
setStatus(node, { fill: 'red', shape: 'ring', text: statusText });
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Attach error metadata to the message for downstream nodes
|
|
154
|
+
msg.error = {
|
|
155
|
+
code: err.code || 'ERROR',
|
|
156
|
+
message: err.message,
|
|
157
|
+
status: err.status || null,
|
|
158
|
+
body: err.body || null,
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
// If the node has a second output, emit error message there instead of throwing
|
|
162
|
+
if (twoOutputs && send) {
|
|
163
|
+
send([null, msg]);
|
|
164
|
+
if (done) done();
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Standard Node-RED error handling (triggers Catch nodes)
|
|
169
|
+
if (done) {
|
|
170
|
+
done(err);
|
|
171
|
+
} else {
|
|
172
|
+
node.error(err.message, msg);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// ─── Common Parameter Builder ────────────────────────────────────────────────
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Build the common query parameters shared by most IPGeo API calls.
|
|
180
|
+
*
|
|
181
|
+
* @param {string} apiKey
|
|
182
|
+
* @param {object} nodeConfig Node configuration properties
|
|
183
|
+
* @returns {object} params object ready for Axios
|
|
184
|
+
*/
|
|
185
|
+
function buildCommonParams(apiKey, nodeConfig) {
|
|
186
|
+
const params = { apiKey };
|
|
187
|
+
|
|
188
|
+
if (nodeConfig.lang && nodeConfig.lang !== 'en') {
|
|
189
|
+
params.lang = nodeConfig.lang;
|
|
190
|
+
}
|
|
191
|
+
if (nodeConfig.output && nodeConfig.output !== 'json') {
|
|
192
|
+
params.output = nodeConfig.output;
|
|
193
|
+
}
|
|
194
|
+
if (nodeConfig.fields && nodeConfig.fields.trim()) {
|
|
195
|
+
params.fields = nodeConfig.fields.trim();
|
|
196
|
+
}
|
|
197
|
+
if (nodeConfig.excludes && nodeConfig.excludes.trim()) {
|
|
198
|
+
params.excludes = nodeConfig.excludes.trim();
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
return params;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// ─── Exports ─────────────────────────────────────────────────────────────────
|
|
205
|
+
|
|
206
|
+
module.exports = {
|
|
207
|
+
STATUS,
|
|
208
|
+
setStatus,
|
|
209
|
+
clearStatus,
|
|
210
|
+
resolveValue,
|
|
211
|
+
setOutputProperty,
|
|
212
|
+
handleNodeError,
|
|
213
|
+
buildCommonParams,
|
|
214
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "node-red-contrib-ipgeolocation",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Node-RED nodes for the IPGeolocation.io v3 API: IP geolocation, security and threat intelligence (VPN, proxy, and TOR detection), abuse contacts, ASN lookup, bulk IP processing, timezone, astronomy, and user-agent parsing.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"node-red",
|
|
7
|
+
"ipgeolocation",
|
|
8
|
+
"geolocation",
|
|
9
|
+
"ip-lookup",
|
|
10
|
+
"timezone",
|
|
11
|
+
"astronomy",
|
|
12
|
+
"user-agent",
|
|
13
|
+
"security",
|
|
14
|
+
"ip-intelligence"
|
|
15
|
+
],
|
|
16
|
+
"author": {
|
|
17
|
+
"name": "IPGeolocation.io"
|
|
18
|
+
},
|
|
19
|
+
"license": "MIT",
|
|
20
|
+
"homepage": "https://github.com/IPGeolocation/node-red-contrib-ipgeolocation",
|
|
21
|
+
"repository": {
|
|
22
|
+
"type": "git",
|
|
23
|
+
"url": "git+https://github.com/IPGeolocation/node-red-contrib-ipgeolocation.git"
|
|
24
|
+
},
|
|
25
|
+
"bugs": {
|
|
26
|
+
"url": "https://github.com/IPGeolocation/node-red-contrib-ipgeolocation/issues"
|
|
27
|
+
},
|
|
28
|
+
"engines": {
|
|
29
|
+
"node": ">=14.0.0"
|
|
30
|
+
},
|
|
31
|
+
"node-red": {
|
|
32
|
+
"version": ">=2.0.0",
|
|
33
|
+
"nodes": {
|
|
34
|
+
"ipgeo-config": "nodes/ipgeo-config.js",
|
|
35
|
+
"ipgeo-lookup": "nodes/ipgeo-lookup.js",
|
|
36
|
+
"ipgeo-bulk": "nodes/ipgeo-bulk.js",
|
|
37
|
+
"ipgeo-security": "nodes/ipgeo-security.js",
|
|
38
|
+
"ipgeo-bulk-security": "nodes/ipgeo-bulk-security.js",
|
|
39
|
+
"ipgeo-abuse": "nodes/ipgeo-abuse.js",
|
|
40
|
+
"ipgeo-asn": "nodes/ipgeo-asn.js",
|
|
41
|
+
"ipgeo-timezone": "nodes/ipgeo-timezone.js",
|
|
42
|
+
"ipgeo-astronomy": "nodes/ipgeo-astronomy.js",
|
|
43
|
+
"ipgeo-useragent": "nodes/ipgeo-useragent.js"
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
"dependencies": {
|
|
47
|
+
"axios": "^1.18.0"
|
|
48
|
+
},
|
|
49
|
+
"devDependencies": {
|
|
50
|
+
"@node-red/editor-client": "^3.1.9",
|
|
51
|
+
"mocha": "^10.8.2",
|
|
52
|
+
"nock": "^13.5.6",
|
|
53
|
+
"node-red": "^3.1.15",
|
|
54
|
+
"node-red-node-test-helper": "^0.3.6",
|
|
55
|
+
"nyc": "^15.1.0",
|
|
56
|
+
"should": "^13.2.3",
|
|
57
|
+
"sinon": "^17.0.1"
|
|
58
|
+
},
|
|
59
|
+
"main": "index.js",
|
|
60
|
+
"files": [
|
|
61
|
+
"nodes/",
|
|
62
|
+
"examples/",
|
|
63
|
+
"index.js",
|
|
64
|
+
"README.md"
|
|
65
|
+
],
|
|
66
|
+
"scripts": {
|
|
67
|
+
"test": "mocha",
|
|
68
|
+
"test:watch": "mocha --watch",
|
|
69
|
+
"coverage": "nyc --reporter=text --reporter=html mocha",
|
|
70
|
+
"prepublishOnly": "npm test"
|
|
71
|
+
}
|
|
72
|
+
}
|