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
package/index.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* node-red-contrib-ipgeolocation
|
|
3
|
+
* Entry point — registers all nodes with the Node-RED runtime.
|
|
4
|
+
*
|
|
5
|
+
* Note: Node-RED loads nodes from the `node-red.nodes` map in package.json,
|
|
6
|
+
* so this file is primarily a convenience for `require()`-ing the package
|
|
7
|
+
* directly (e.g. in tests). It must stay in sync with package.json.
|
|
8
|
+
*/
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
module.exports = function (RED) {
|
|
12
|
+
require('./nodes/ipgeo-config')(RED);
|
|
13
|
+
require('./nodes/ipgeo-lookup')(RED);
|
|
14
|
+
require('./nodes/ipgeo-bulk')(RED);
|
|
15
|
+
require('./nodes/ipgeo-security')(RED);
|
|
16
|
+
require('./nodes/ipgeo-bulk-security')(RED);
|
|
17
|
+
require('./nodes/ipgeo-abuse')(RED);
|
|
18
|
+
require('./nodes/ipgeo-asn')(RED);
|
|
19
|
+
require('./nodes/ipgeo-timezone')(RED);
|
|
20
|
+
require('./nodes/ipgeo-astronomy')(RED);
|
|
21
|
+
require('./nodes/ipgeo-useragent')(RED);
|
|
22
|
+
};
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
<!-- ============================================================
|
|
2
|
+
ipgeo-abuse.html
|
|
3
|
+
Single-IP abuse contact lookup node — editor template + help
|
|
4
|
+
============================================================ -->
|
|
5
|
+
|
|
6
|
+
<script type="text/javascript">
|
|
7
|
+
(function () {
|
|
8
|
+
'use strict';
|
|
9
|
+
|
|
10
|
+
RED.nodes.registerType('ipgeo-abuse', {
|
|
11
|
+
category: 'IPGeolocation',
|
|
12
|
+
color: '#c2554f',
|
|
13
|
+
icon: 'font-awesome/fa-envelope',
|
|
14
|
+
paletteLabel:'ipgeo abuse',
|
|
15
|
+
|
|
16
|
+
defaults: {
|
|
17
|
+
name: { value: '' },
|
|
18
|
+
server: { value: '', type: 'ipgeo-config', required: true },
|
|
19
|
+
ipType: { value: 'str' },
|
|
20
|
+
ipValue: { value: '' },
|
|
21
|
+
fields: { value: '' },
|
|
22
|
+
excludes: { value: '' },
|
|
23
|
+
outputProp: { value: 'payload' },
|
|
24
|
+
outputType: { value: 'msg' },
|
|
25
|
+
twoOutputs: { value: true },
|
|
26
|
+
},
|
|
27
|
+
|
|
28
|
+
inputs: 1,
|
|
29
|
+
outputs: 2, // [success, error]
|
|
30
|
+
|
|
31
|
+
outputLabels: function (idx) {
|
|
32
|
+
return idx === 0 ? 'success' : 'error';
|
|
33
|
+
},
|
|
34
|
+
|
|
35
|
+
label: function () {
|
|
36
|
+
return this.name || 'ipgeo abuse';
|
|
37
|
+
},
|
|
38
|
+
|
|
39
|
+
labelStyle: function () {
|
|
40
|
+
return this.name ? 'node_label_italic' : '';
|
|
41
|
+
},
|
|
42
|
+
|
|
43
|
+
oneditprepare: function () {
|
|
44
|
+
const node = this;
|
|
45
|
+
|
|
46
|
+
// ── IP input — typed input widget ──────────────────────────────────
|
|
47
|
+
$('#node-input-ipValue').typedInput({
|
|
48
|
+
default: 'str',
|
|
49
|
+
typeField: '#node-input-ipType',
|
|
50
|
+
types: ['msg', 'flow', 'global', 'str'],
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
// ── Output property typed input ────────────────────────────────────
|
|
54
|
+
$('#node-input-outputProp').typedInput({
|
|
55
|
+
default: 'msg',
|
|
56
|
+
typeField: '#node-input-outputType',
|
|
57
|
+
types: ['msg', 'flow', 'global'],
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
// ── Two outputs checkbox ───────────────────────────────────────────
|
|
61
|
+
$('#node-input-twoOutputs').prop('checked', node.twoOutputs !== false);
|
|
62
|
+
},
|
|
63
|
+
|
|
64
|
+
oneditsave: function () {
|
|
65
|
+
this.twoOutputs = $('#node-input-twoOutputs').prop('checked');
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
}());
|
|
69
|
+
</script>
|
|
70
|
+
|
|
71
|
+
<!-- ── Edit template ── -->
|
|
72
|
+
<script type="text/html" data-template-name="ipgeo-abuse">
|
|
73
|
+
<!-- Name -->
|
|
74
|
+
<div class="form-row">
|
|
75
|
+
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
|
|
76
|
+
<input type="text" id="node-input-name" placeholder="ipgeo abuse" style="width:70%"/>
|
|
77
|
+
</div>
|
|
78
|
+
|
|
79
|
+
<!-- Config node (shared with ipgeo-lookup / ipgeo-security) -->
|
|
80
|
+
<div class="form-row">
|
|
81
|
+
<label for="node-input-server"><i class="fa fa-key"></i> API Config</label>
|
|
82
|
+
<input type="text" id="node-input-server" style="width:70%"/>
|
|
83
|
+
<input type="hidden" id="node-input-ipType"/>
|
|
84
|
+
</div>
|
|
85
|
+
|
|
86
|
+
<hr/>
|
|
87
|
+
|
|
88
|
+
<!-- IP address input -->
|
|
89
|
+
<div class="form-row">
|
|
90
|
+
<label for="node-input-ipValue"><i class="fa fa-globe"></i> IP Address</label>
|
|
91
|
+
<input type="text" id="node-input-ipValue" placeholder="Leave empty to detect caller IP" style="width:70%"/>
|
|
92
|
+
<div style="margin-top:4px;font-size:0.82em;color:#888;">
|
|
93
|
+
IPv4 or IPv6 only. Leave empty to auto-detect the caller's IP. You
|
|
94
|
+
can also point this to a msg/flow/global property.
|
|
95
|
+
</div>
|
|
96
|
+
</div>
|
|
97
|
+
|
|
98
|
+
<hr/>
|
|
99
|
+
|
|
100
|
+
<!-- Fields & Excludes -->
|
|
101
|
+
<div class="form-row">
|
|
102
|
+
<label for="node-input-fields"><i class="fa fa-filter"></i> Fields</label>
|
|
103
|
+
<input type="text" id="node-input-fields" placeholder="e.g. abuse.emails,abuse.phone_numbers" style="width:70%"/>
|
|
104
|
+
</div>
|
|
105
|
+
<div class="form-row">
|
|
106
|
+
<label for="node-input-excludes"><i class="fa fa-minus-square"></i> Excludes</label>
|
|
107
|
+
<input type="text" id="node-input-excludes" placeholder="e.g. abuse.address" style="width:70%"/>
|
|
108
|
+
</div>
|
|
109
|
+
|
|
110
|
+
<hr/>
|
|
111
|
+
|
|
112
|
+
<!-- Output property -->
|
|
113
|
+
<div class="form-row">
|
|
114
|
+
<label for="node-input-outputProp"><i class="fa fa-sign-out"></i> Output to</label>
|
|
115
|
+
<input type="text" id="node-input-outputProp" style="width:70%"/>
|
|
116
|
+
<input type="hidden" id="node-input-outputType"/>
|
|
117
|
+
</div>
|
|
118
|
+
|
|
119
|
+
<!-- Two outputs toggle -->
|
|
120
|
+
<div class="form-row">
|
|
121
|
+
<label><i class="fa fa-random"></i> Outputs</label>
|
|
122
|
+
<input type="checkbox" id="node-input-twoOutputs" style="width:auto;margin-right:6px"/>
|
|
123
|
+
<label for="node-input-twoOutputs" style="display:inline;font-weight:normal">
|
|
124
|
+
Use second output for errors (instead of throwing to Catch node)
|
|
125
|
+
</label>
|
|
126
|
+
</div>
|
|
127
|
+
</script>
|
|
128
|
+
|
|
129
|
+
<!-- ── Help panel ── -->
|
|
130
|
+
<script type="text/html" data-help-name="ipgeo-abuse">
|
|
131
|
+
<p>
|
|
132
|
+
Looks up abuse-reporting contact details for a single IPv4 or IPv6
|
|
133
|
+
address via <strong>IPGeolocation.io v3 Abuse Contact API</strong>:
|
|
134
|
+
responsible organization, abuse-handling role, contact emails, phone
|
|
135
|
+
numbers, and registered address.
|
|
136
|
+
</p>
|
|
137
|
+
|
|
138
|
+
<h3>Inputs</h3>
|
|
139
|
+
<dl class="message-properties">
|
|
140
|
+
<dt>payload <span class="property-type">any</span></dt>
|
|
141
|
+
<dd>Passed through unchanged.</dd>
|
|
142
|
+
</dl>
|
|
143
|
+
|
|
144
|
+
<h3>Outputs</h3>
|
|
145
|
+
<ol class="node-ports">
|
|
146
|
+
<li>
|
|
147
|
+
<strong>Success</strong>
|
|
148
|
+
<dl class="message-properties">
|
|
149
|
+
<dt>payload <span class="property-type">object</span></dt>
|
|
150
|
+
<dd>
|
|
151
|
+
<code>{ ip, abuse }</code> where <code>abuse</code> contains
|
|
152
|
+
<code>route</code>, <code>country</code>, <code>name</code>,
|
|
153
|
+
<code>organization</code>, <code>kind</code>
|
|
154
|
+
(<code>group</code> | <code>individual</code>),
|
|
155
|
+
<code>address</code>, <code>emails</code> (array),
|
|
156
|
+
and <code>phone_numbers</code> (array).
|
|
157
|
+
</dd>
|
|
158
|
+
<dt class="optional">ipgeo_credits <span class="property-type">number</span></dt>
|
|
159
|
+
<dd>Credits consumed by this request (flat 1 credit per lookup).</dd>
|
|
160
|
+
</dl>
|
|
161
|
+
</li>
|
|
162
|
+
<li>
|
|
163
|
+
<strong>Error</strong> (when <em>Use second output</em> is checked)
|
|
164
|
+
<dl class="message-properties">
|
|
165
|
+
<dt>error <span class="property-type">object</span></dt>
|
|
166
|
+
<dd>
|
|
167
|
+
<code>{ code, message, status, body }</code> — structured error object.
|
|
168
|
+
Error codes include <code>AUTH_FAILED</code>, <code>RATE_LIMITED</code>,
|
|
169
|
+
<code>TIMEOUT</code>, <code>BAD_REQUEST</code>, etc.
|
|
170
|
+
</dd>
|
|
171
|
+
</dl>
|
|
172
|
+
</li>
|
|
173
|
+
</ol>
|
|
174
|
+
|
|
175
|
+
<h3>Details</h3>
|
|
176
|
+
<ul>
|
|
177
|
+
<li>Leave <em>IP Address</em> empty to look up the caller's IP address.</li>
|
|
178
|
+
<li>Every successful lookup costs a flat <strong>1 credit</strong>.</li>
|
|
179
|
+
<li>
|
|
180
|
+
<em>Fields</em> restricts the response to named fields
|
|
181
|
+
(e.g. <code>abuse.emails</code>).
|
|
182
|
+
</li>
|
|
183
|
+
<li>
|
|
184
|
+
<em>Excludes</em> removes fields from the <code>abuse</code> object
|
|
185
|
+
(e.g. <code>abuse.address</code>). The top-level <code>ip</code>
|
|
186
|
+
field cannot be excluded.
|
|
187
|
+
</li>
|
|
188
|
+
<li>Any field may legitimately come back empty if the registry has no data for it.</li>
|
|
189
|
+
<li>
|
|
190
|
+
Bogon/private IPs are rejected with HTTP 423 and are not billed.
|
|
191
|
+
</li>
|
|
192
|
+
<li>Failed requests are retried up to 3 times with exponential back-off.</li>
|
|
193
|
+
</ul>
|
|
194
|
+
|
|
195
|
+
<h3>References</h3>
|
|
196
|
+
<ul>
|
|
197
|
+
<li>
|
|
198
|
+
<a href="https://ipgeolocation.io/documentation/ip-abuse-contact-api.html" target="_blank">
|
|
199
|
+
IP Abuse Contact API documentation
|
|
200
|
+
</a>
|
|
201
|
+
</li>
|
|
202
|
+
</ul>
|
|
203
|
+
</script>
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ipgeo-abuse.js
|
|
3
|
+
*
|
|
4
|
+
* Node-RED node: "ipgeo abuse"
|
|
5
|
+
*
|
|
6
|
+
* Performs a single-IP abuse contact lookup via the IPGeolocation.io
|
|
7
|
+
* /v3/abuse endpoint (GET).
|
|
8
|
+
*
|
|
9
|
+
* Features
|
|
10
|
+
* ─────────
|
|
11
|
+
* • IPv4 or IPv6 input, or empty (caller IP auto-detect)
|
|
12
|
+
* • `fields` and `excludes` for bandwidth optimisation (dot-notation,
|
|
13
|
+
* e.g. abuse.emails / abuse.phone_numbers)
|
|
14
|
+
* • IP can be sourced from node config OR msg property (dynamic)
|
|
15
|
+
* • Two outputs: [success, error] — error output is optional
|
|
16
|
+
* • Exposes credits consumed on msg.ipgeo_credits (flat 1 credit/lookup)
|
|
17
|
+
* • Full retry + exponential backoff via shared api-client
|
|
18
|
+
*
|
|
19
|
+
* Shares the same `ipgeo-config` config node (API key) as ipgeo-lookup
|
|
20
|
+
* and ipgeo-security.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
'use strict';
|
|
24
|
+
|
|
25
|
+
const { request, IPGeoError } = require('./lib/api-client');
|
|
26
|
+
const {
|
|
27
|
+
setStatus,
|
|
28
|
+
resolveValue,
|
|
29
|
+
setOutputProperty,
|
|
30
|
+
handleNodeError,
|
|
31
|
+
buildCommonParams,
|
|
32
|
+
} = require('./lib/node-helpers');
|
|
33
|
+
|
|
34
|
+
module.exports = function (RED) {
|
|
35
|
+
|
|
36
|
+
function IPGeoAbuseNode(config) {
|
|
37
|
+
RED.nodes.createNode(this, config);
|
|
38
|
+
|
|
39
|
+
// ── Config node (API key) ─────────────────────────────────────────────
|
|
40
|
+
const configNode = RED.nodes.getNode(config.server);
|
|
41
|
+
|
|
42
|
+
// ── Node configuration ────────────────────────────────────────────────
|
|
43
|
+
this.name = config.name;
|
|
44
|
+
this.ipType = config.ipType || 'str'; // 'str' | 'msg' | 'flow' | 'global'
|
|
45
|
+
this.ipValue = config.ipValue || ''; // value or msg/flow/global prop path
|
|
46
|
+
this.fields = config.fields || '';
|
|
47
|
+
this.excludes = config.excludes || '';
|
|
48
|
+
this.outputProp = config.outputProp || 'payload'; // output property
|
|
49
|
+
this.outputType = config.outputType || 'msg';
|
|
50
|
+
this.twoOutputs = config.twoOutputs !== false; // default true
|
|
51
|
+
|
|
52
|
+
const node = this;
|
|
53
|
+
|
|
54
|
+
// ── Pre-flight: warn if no config node ───────────────────────────────
|
|
55
|
+
if (!configNode) {
|
|
56
|
+
setStatus(node, 'NO_KEY');
|
|
57
|
+
node.warn('No IPGeolocation.io config node selected — configure an API key first.');
|
|
58
|
+
} else {
|
|
59
|
+
setStatus(node, 'IDLE');
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// ── Input handler ─────────────────────────────────────────────────────
|
|
63
|
+
node.on('input', async function (msg, send, done) {
|
|
64
|
+
// Node-RED 0.x compat
|
|
65
|
+
send = send || function () { node.send.apply(node, arguments); };
|
|
66
|
+
|
|
67
|
+
// ── Validate API key availability ─────────────────────────────────
|
|
68
|
+
const apiKey = configNode && configNode.credentials
|
|
69
|
+
? configNode.credentials.apiKey
|
|
70
|
+
: null;
|
|
71
|
+
|
|
72
|
+
if (!apiKey) {
|
|
73
|
+
setStatus(node, 'NO_KEY');
|
|
74
|
+
const err = new IPGeoError('No API key configured', 'NO_API_KEY');
|
|
75
|
+
handleNodeError(node, err, msg, done, send, node.twoOutputs);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// ── Resolve IP from msg/flow/global or static config ──────────────
|
|
80
|
+
let ip = '';
|
|
81
|
+
if (node.ipType === 'msg') {
|
|
82
|
+
ip = resolveValue(RED, node, msg,
|
|
83
|
+
{ value: node.ipValue, type: 'msg' }) || '';
|
|
84
|
+
} else if (node.ipType === 'flow') {
|
|
85
|
+
ip = resolveValue(RED, node, msg,
|
|
86
|
+
{ value: node.ipValue, type: 'flow' }) || '';
|
|
87
|
+
} else if (node.ipType === 'global') {
|
|
88
|
+
ip = resolveValue(RED, node, msg,
|
|
89
|
+
{ value: node.ipValue, type: 'global' }) || '';
|
|
90
|
+
} else {
|
|
91
|
+
// 'str' — static value from node config
|
|
92
|
+
ip = (node.ipValue || '').trim();
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
ip = String(ip).trim();
|
|
96
|
+
|
|
97
|
+
// ── Build query parameters ────────────────────────────────────────
|
|
98
|
+
// Abuse endpoint has no `lang` or `include` params — just
|
|
99
|
+
// apiKey / ip / fields / excludes. Like /ipgeo and /security, an
|
|
100
|
+
// omitted ip auto-detects the caller's IP.
|
|
101
|
+
const params = buildCommonParams(apiKey, {
|
|
102
|
+
fields: node.fields,
|
|
103
|
+
excludes: node.excludes,
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
if (ip) params.ip = ip;
|
|
107
|
+
|
|
108
|
+
// ── Set working status ────────────────────────────────────────────
|
|
109
|
+
setStatus(node, 'WORKING');
|
|
110
|
+
|
|
111
|
+
// ── API call ──────────────────────────────────────────────────────
|
|
112
|
+
try {
|
|
113
|
+
const { data, credits } = await request({
|
|
114
|
+
method: 'GET',
|
|
115
|
+
path: '/abuse',
|
|
116
|
+
params,
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
// ── Success ────────────────────────────────────────────────────
|
|
120
|
+
setStatus(node, 'OK', data.abuse?.organization || data.abuse?.name || data.ip || 'ok');
|
|
121
|
+
|
|
122
|
+
// Write result to configured output property
|
|
123
|
+
setOutputProperty(RED, node, msg,
|
|
124
|
+
{ value: node.outputProp, type: node.outputType }, data);
|
|
125
|
+
|
|
126
|
+
// Expose credits consumed (informational) — flat 1 credit/lookup
|
|
127
|
+
if (credits !== null) msg.ipgeo_credits = credits;
|
|
128
|
+
|
|
129
|
+
// Clear error property if previously set
|
|
130
|
+
delete msg.error;
|
|
131
|
+
|
|
132
|
+
send(node.twoOutputs ? [msg, null] : msg);
|
|
133
|
+
if (done) done();
|
|
134
|
+
|
|
135
|
+
} catch (err) {
|
|
136
|
+
handleNodeError(node, err, msg, done, send, node.twoOutputs);
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
// ── Clean up on node removal/redeploy ─────────────────────────────────
|
|
141
|
+
node.on('close', function () {
|
|
142
|
+
setStatus(node, 'IDLE');
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
RED.nodes.registerType('ipgeo-abuse', IPGeoAbuseNode);
|
|
147
|
+
|
|
148
|
+
};
|
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
<!-- ============================================================
|
|
2
|
+
ipgeo-asn.html
|
|
3
|
+
ASN lookup node (by IP or by ASN number) — editor template + help
|
|
4
|
+
============================================================ -->
|
|
5
|
+
|
|
6
|
+
<script type="text/javascript">
|
|
7
|
+
(function () {
|
|
8
|
+
'use strict';
|
|
9
|
+
|
|
10
|
+
// ── Include module list ────────────────────────────────────────────────────
|
|
11
|
+
const INCLUDE_MODULES = [
|
|
12
|
+
{ id: 'inc-peers', val: 'peers', label: 'peers' },
|
|
13
|
+
{ id: 'inc-downstreams', val: 'downstreams', label: 'downstreams' },
|
|
14
|
+
{ id: 'inc-upstreams', val: 'upstreams', label: 'upstreams' },
|
|
15
|
+
{ id: 'inc-routes', val: 'routes', label: 'routes' },
|
|
16
|
+
{ id: 'inc-whois', val: 'whois_response', label: 'whois_response' },
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
RED.nodes.registerType('ipgeo-asn', {
|
|
20
|
+
category: 'IPGeolocation',
|
|
21
|
+
color: '#7aa874',
|
|
22
|
+
icon: 'font-awesome/fa-sitemap',
|
|
23
|
+
paletteLabel: 'ipgeo asn',
|
|
24
|
+
|
|
25
|
+
defaults: {
|
|
26
|
+
name: { value: '' },
|
|
27
|
+
server: { value: '', type: 'ipgeo-config', required: true },
|
|
28
|
+
queryType: { value: 'auto' },
|
|
29
|
+
ipType: { value: 'str' },
|
|
30
|
+
ipValue: { value: '' },
|
|
31
|
+
asnType: { value: 'str' },
|
|
32
|
+
asnValue: { value: '' },
|
|
33
|
+
include: { value: '' },
|
|
34
|
+
fields: { value: '' },
|
|
35
|
+
excludes: { value: '' },
|
|
36
|
+
outputProp: { value: 'payload' },
|
|
37
|
+
outputType: { value: 'msg' },
|
|
38
|
+
twoOutputs: { value: true },
|
|
39
|
+
},
|
|
40
|
+
|
|
41
|
+
inputs: 1,
|
|
42
|
+
outputs: 2, // [success, error]
|
|
43
|
+
|
|
44
|
+
outputLabels: function (idx) {
|
|
45
|
+
return idx === 0 ? 'success' : 'error';
|
|
46
|
+
},
|
|
47
|
+
|
|
48
|
+
label: function () {
|
|
49
|
+
return this.name || 'ipgeo asn';
|
|
50
|
+
},
|
|
51
|
+
|
|
52
|
+
labelStyle: function () {
|
|
53
|
+
return this.name ? 'node_label_italic' : '';
|
|
54
|
+
},
|
|
55
|
+
|
|
56
|
+
oneditprepare: function () {
|
|
57
|
+
const node = this;
|
|
58
|
+
|
|
59
|
+
// ── IP input — typed input widget ──────────────────────────────────
|
|
60
|
+
$('#node-input-ipValue').typedInput({
|
|
61
|
+
default: 'str',
|
|
62
|
+
typeField: '#node-input-ipType',
|
|
63
|
+
types: ['msg', 'flow', 'global', 'str'],
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
// ── ASN input — typed input widget ─────────────────────────────────
|
|
67
|
+
$('#node-input-asnValue').typedInput({
|
|
68
|
+
default: 'str',
|
|
69
|
+
typeField: '#node-input-asnType',
|
|
70
|
+
types: ['msg', 'flow', 'global', 'str'],
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
// ── Query type — show/hide the relevant row ─────────────────────────
|
|
74
|
+
function toggleQueryRows() {
|
|
75
|
+
const qt = $('#node-input-queryType').val();
|
|
76
|
+
$('#ipgeo-asn-ip-row').toggle(qt === 'ip');
|
|
77
|
+
$('#ipgeo-asn-asn-row').toggle(qt === 'asn');
|
|
78
|
+
}
|
|
79
|
+
$('#node-input-queryType').on('change', toggleQueryRows);
|
|
80
|
+
toggleQueryRows();
|
|
81
|
+
|
|
82
|
+
// ── Include checkboxes — encode/decode comma list ──────────────────
|
|
83
|
+
const incList = (node.include || '').split(',').map(s => s.trim()).filter(Boolean);
|
|
84
|
+
INCLUDE_MODULES.forEach(function (mod) {
|
|
85
|
+
const $cb = $('<input type="checkbox" style="margin-right:6px">')
|
|
86
|
+
.prop('id', mod.id)
|
|
87
|
+
.prop('checked', incList.includes(mod.val))
|
|
88
|
+
.data('val', mod.val);
|
|
89
|
+
const $label = $('<label>').attr('for', mod.id).text(mod.label)
|
|
90
|
+
.css({ display: 'inline', fontWeight: 'normal', marginRight: '12px' });
|
|
91
|
+
$('#ipgeo-asn-include-container').append($('<div>').css('display', 'inline-block').append($cb).append($label));
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
// ── Output property typed input ────────────────────────────────────
|
|
95
|
+
$('#node-input-outputProp').typedInput({
|
|
96
|
+
default: 'msg',
|
|
97
|
+
typeField: '#node-input-outputType',
|
|
98
|
+
types: ['msg', 'flow', 'global'],
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
// ── Two outputs checkbox ───────────────────────────────────────────
|
|
102
|
+
$('#node-input-twoOutputs').prop('checked', node.twoOutputs !== false);
|
|
103
|
+
},
|
|
104
|
+
|
|
105
|
+
oneditsave: function () {
|
|
106
|
+
// Collect include checkboxes back into a comma-separated string
|
|
107
|
+
const chosen = [];
|
|
108
|
+
INCLUDE_MODULES.forEach(function (mod) {
|
|
109
|
+
if ($('#' + mod.id).prop('checked')) chosen.push(mod.val);
|
|
110
|
+
});
|
|
111
|
+
this.include = chosen.join(',');
|
|
112
|
+
this.twoOutputs = $('#node-input-twoOutputs').prop('checked');
|
|
113
|
+
},
|
|
114
|
+
});
|
|
115
|
+
}());
|
|
116
|
+
</script>
|
|
117
|
+
|
|
118
|
+
<!-- ── Edit template ── -->
|
|
119
|
+
<script type="text/html" data-template-name="ipgeo-asn">
|
|
120
|
+
<!-- Name -->
|
|
121
|
+
<div class="form-row">
|
|
122
|
+
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
|
|
123
|
+
<input type="text" id="node-input-name" placeholder="ipgeo asn" style="width:70%" />
|
|
124
|
+
</div>
|
|
125
|
+
|
|
126
|
+
<!-- Config node (shared with other ipgeo-* nodes) -->
|
|
127
|
+
<div class="form-row">
|
|
128
|
+
<label for="node-input-server"><i class="fa fa-key"></i> API Config</label>
|
|
129
|
+
<input type="text" id="node-input-server" style="width:70%" />
|
|
130
|
+
</div>
|
|
131
|
+
|
|
132
|
+
<hr />
|
|
133
|
+
|
|
134
|
+
<!-- Query mode -->
|
|
135
|
+
<div class="form-row">
|
|
136
|
+
<label for="node-input-queryType"><i class="fa fa-search"></i> Lookup by</label>
|
|
137
|
+
<select id="node-input-queryType" style="width:70%">
|
|
138
|
+
<option value="auto">Auto-detect (caller's IP)</option>
|
|
139
|
+
<option value="ip">IP address</option>
|
|
140
|
+
<option value="asn">ASN number</option>
|
|
141
|
+
</select>
|
|
142
|
+
</div>
|
|
143
|
+
|
|
144
|
+
<!-- IP row (shown when queryType === 'ip') -->
|
|
145
|
+
<div class="form-row" id="ipgeo-asn-ip-row">
|
|
146
|
+
<label for="node-input-ipValue" style="margin-left:20px"><i class="fa fa-globe"></i> IP Address</label>
|
|
147
|
+
<input type="text" id="node-input-ipValue" placeholder="e.g. 49.12.0.0 or msg.ip" style="width:60%" />
|
|
148
|
+
<input type="hidden" id="node-input-ipType" />
|
|
149
|
+
</div>
|
|
150
|
+
|
|
151
|
+
<!-- ASN row (shown when queryType === 'asn') -->
|
|
152
|
+
<div class="form-row" id="ipgeo-asn-asn-row">
|
|
153
|
+
<label for="node-input-asnValue" style="margin-left:20px"><i class="fa fa-hashtag"></i> ASN</label>
|
|
154
|
+
<input type="text" id="node-input-asnValue" placeholder="e.g. 24940 or AS24940" style="width:60%" />
|
|
155
|
+
<input type="hidden" id="node-input-asnType" />
|
|
156
|
+
</div>
|
|
157
|
+
|
|
158
|
+
<hr />
|
|
159
|
+
|
|
160
|
+
<!-- Include modules (checkboxes) -->
|
|
161
|
+
<div class="form-row">
|
|
162
|
+
<label><i class="fa fa-plus-square"></i> Include</label>
|
|
163
|
+
<div id="ipgeo-asn-include-container" style="display:inline-block;width:70%;vertical-align:top;line-height:2em">
|
|
164
|
+
</div>
|
|
165
|
+
</div>
|
|
166
|
+
<!-- Hidden field intentionally omitted — include is built/read entirely
|
|
167
|
+
via the checkboxes above in oneditprepare/oneditsave. -->
|
|
168
|
+
|
|
169
|
+
<hr />
|
|
170
|
+
|
|
171
|
+
<!-- Fields & Excludes -->
|
|
172
|
+
<div class="form-row">
|
|
173
|
+
<label for="node-input-fields"><i class="fa fa-filter"></i> Fields</label>
|
|
174
|
+
<input type="text" id="node-input-fields" placeholder="e.g. asn.organization,asn.country" style="width:70%" />
|
|
175
|
+
</div>
|
|
176
|
+
<div class="form-row">
|
|
177
|
+
<label for="node-input-excludes"><i class="fa fa-minus-square"></i> Excludes</label>
|
|
178
|
+
<input type="text" id="node-input-excludes" placeholder="e.g. asn.date_allocated,asn.allocation_status"
|
|
179
|
+
style="width:70%" />
|
|
180
|
+
</div>
|
|
181
|
+
|
|
182
|
+
<hr />
|
|
183
|
+
|
|
184
|
+
<!-- Output property -->
|
|
185
|
+
<div class="form-row">
|
|
186
|
+
<label for="node-input-outputProp"><i class="fa fa-sign-out"></i> Output to</label>
|
|
187
|
+
<input type="text" id="node-input-outputProp" style="width:70%" />
|
|
188
|
+
<input type="hidden" id="node-input-outputType" />
|
|
189
|
+
</div>
|
|
190
|
+
|
|
191
|
+
<!-- Two outputs toggle -->
|
|
192
|
+
<div class="form-row">
|
|
193
|
+
<label><i class="fa fa-random"></i> Outputs</label>
|
|
194
|
+
<input type="checkbox" id="node-input-twoOutputs" style="width:auto;margin-right:6px" />
|
|
195
|
+
<label for="node-input-twoOutputs" style="display:inline;font-weight:normal">
|
|
196
|
+
Use second output for errors (instead of throwing to Catch node)
|
|
197
|
+
</label>
|
|
198
|
+
</div>
|
|
199
|
+
</script>
|
|
200
|
+
|
|
201
|
+
<!-- ── Help panel ── -->
|
|
202
|
+
<script type="text/html" data-help-name="ipgeo-asn">
|
|
203
|
+
<p>
|
|
204
|
+
Looks up Autonomous System Number (ASN) details — organization, country,
|
|
205
|
+
type, allocation date, RIR, and (optionally) peers, upstreams,
|
|
206
|
+
downstreams, routes, and raw WHOIS text — via
|
|
207
|
+
<strong>IPGeolocation.io v3 ASN API</strong>.
|
|
208
|
+
</p>
|
|
209
|
+
|
|
210
|
+
<h3>Inputs</h3>
|
|
211
|
+
<dl class="message-properties">
|
|
212
|
+
<dt>payload <span class="property-type">any</span></dt>
|
|
213
|
+
<dd>Passed through unchanged.</dd>
|
|
214
|
+
<dt class="optional">ipgeo_include <span class="property-type">string</span></dt>
|
|
215
|
+
<dd>
|
|
216
|
+
Override the node's <em>Include</em> setting per-message.
|
|
217
|
+
Comma-separated list, e.g. <code>"peers,routes"</code>.
|
|
218
|
+
</dd>
|
|
219
|
+
</dl>
|
|
220
|
+
|
|
221
|
+
<h3>Outputs</h3>
|
|
222
|
+
<ol class="node-ports">
|
|
223
|
+
<li>
|
|
224
|
+
<strong>Success</strong>
|
|
225
|
+
<dl class="message-properties">
|
|
226
|
+
<dt>payload <span class="property-type">object</span></dt>
|
|
227
|
+
<dd>
|
|
228
|
+
<code>{ asn, ip? }</code> — <code>ip</code> is present only when
|
|
229
|
+
looking up by IP address or auto-detected caller IP; it is
|
|
230
|
+
omitted when looking up directly by ASN number.
|
|
231
|
+
<code>asn</code> contains <code>as_number</code>,
|
|
232
|
+
<code>organization</code>, <code>country</code>,
|
|
233
|
+
<code>asn_name</code>, <code>type</code>, <code>domain</code>,
|
|
234
|
+
<code>date_allocated</code>, <code>rir</code>,
|
|
235
|
+
<code>allocation_status</code>, <code>num_of_ipv4_routes</code>,
|
|
236
|
+
<code>num_of_ipv6_routes</code>, and (when included)
|
|
237
|
+
<code>peers</code>, <code>downstreams</code>,
|
|
238
|
+
<code>upstreams</code>, <code>routes</code>,
|
|
239
|
+
<code>whois_response</code>.
|
|
240
|
+
</dd>
|
|
241
|
+
<dt class="optional">ipgeo_credits <span class="property-type">number</span></dt>
|
|
242
|
+
<dd>Credits consumed by this request (flat 1 credit per lookup).</dd>
|
|
243
|
+
</dl>
|
|
244
|
+
</li>
|
|
245
|
+
<li>
|
|
246
|
+
<strong>Error</strong> (when <em>Use second output</em> is checked)
|
|
247
|
+
<dl class="message-properties">
|
|
248
|
+
<dt>error <span class="property-type">object</span></dt>
|
|
249
|
+
<dd>
|
|
250
|
+
<code>{ code, message, status, body }</code> — structured error object.
|
|
251
|
+
Error codes include <code>AUTH_FAILED</code>, <code>RATE_LIMITED</code>,
|
|
252
|
+
<code>TIMEOUT</code>, <code>BAD_REQUEST</code>, etc.
|
|
253
|
+
</dd>
|
|
254
|
+
</dl>
|
|
255
|
+
</li>
|
|
256
|
+
</ol>
|
|
257
|
+
|
|
258
|
+
<h3>Details</h3>
|
|
259
|
+
<ul>
|
|
260
|
+
<li>
|
|
261
|
+
<em>Lookup by</em> chooses which single parameter is sent:
|
|
262
|
+
<strong>Auto-detect</strong> sends neither <code>ip</code> nor
|
|
263
|
+
<code>asn</code> and the API resolves the caller's IP;
|
|
264
|
+
<strong>IP address</strong> sends <code>ip</code>;
|
|
265
|
+
<strong>ASN number</strong> sends <code>asn</code> (the
|
|
266
|
+
<code>AS</code> prefix, if typed, is stripped automatically).
|
|
267
|
+
</li>
|
|
268
|
+
<li>
|
|
269
|
+
Use <em>Include</em> checkboxes to add <code>peers</code>,
|
|
270
|
+
<code>downstreams</code>, <code>upstreams</code>, <code>routes</code>,
|
|
271
|
+
and/or <code>whois_response</code> to the response. None of these
|
|
272
|
+
add extra credit cost — the lookup is always flat-rate.
|
|
273
|
+
</li>
|
|
274
|
+
<li>
|
|
275
|
+
<em>Fields</em> restricts the response to named fields
|
|
276
|
+
(e.g. <code>asn.organization,asn.country</code>). If both
|
|
277
|
+
<em>Include</em> and <em>Fields</em> are set, only <em>Include</em>
|
|
278
|
+
is applied by the API.
|
|
279
|
+
</li>
|
|
280
|
+
<li>
|
|
281
|
+
<em>Excludes</em> removes fields from the response
|
|
282
|
+
(e.g. <code>asn.date_allocated,asn.allocation_status</code>). The
|
|
283
|
+
top-level <code>ip</code> field cannot be excluded.
|
|
284
|
+
</li>
|
|
285
|
+
<li>Every successful lookup costs a flat <strong>1 credit</strong>.</li>
|
|
286
|
+
<li>Failed requests are retried up to 3 times with exponential back-off.</li>
|
|
287
|
+
</ul>
|
|
288
|
+
|
|
289
|
+
<h3>References</h3>
|
|
290
|
+
<ul>
|
|
291
|
+
<li>
|
|
292
|
+
<a href="https://ipgeolocation.io/documentation/asn-api.html" target="_blank">
|
|
293
|
+
ASN API documentation
|
|
294
|
+
</a>
|
|
295
|
+
</li>
|
|
296
|
+
</ul>
|
|
297
|
+
</script>
|