external-ip
Advanced tools
Comparing version 1.3.1 to 2.0.0
@@ -7,5 +7,5 @@ #!/usr/bin/env node | ||
const pkg = require('../package.json'); | ||
const defaultConfig = require('./defaultConfig'); | ||
const configSchema = require('./configSchema.json').properties; | ||
const collect = (service, services) => { | ||
const collect = (service, services = []) => { | ||
services.push(service); | ||
@@ -18,4 +18,12 @@ return services; | ||
program.on('--help', () => { | ||
console.log(` | ||
program.usage('[options]') | ||
.version(pkg.version) | ||
.option('-R, --replace', 'replace internal services instead of extending them.') | ||
.option('-s, --services <url>', 'service url, see examples, required if using -R', collect) | ||
.option('-t, --timeout <ms>', 'set timeout per request', parseInt) | ||
.option('-P, --parallel', 'set to parallel mode') | ||
.option('-u, --userAgent <User-Agent>', `provide a User-Agent header, default: ${configSchema.userAgent.default}`, null, '') | ||
.option('-v, --verbose', 'provide additional details') | ||
.on('--help', () => { | ||
console.log(` | ||
This program prints the external IP of the machine. | ||
@@ -25,47 +33,28 @@ All arguments are optional.\n | ||
$ external-ip | ||
$ external-ip -P -t 1500 -R -s ${defaultConfig.services[0]} -s ${defaultConfig.services[1]}\n | ||
$ external-ip -P -t 1500 -R -s ${configSchema.services.default[0]} -s ${configSchema.services.default[1]}\n | ||
Default services: | ||
${defaultConfig.services.join('\n \t')}\n | ||
${configSchema.services.default.join('\n \t')}\n | ||
Documentation can be found at ${pkg.homepage}\n`); | ||
}); | ||
program | ||
.usage('[options]') | ||
.version(pkg.version) | ||
.option('-R, --replace', 'replace internal services instead of extending them.') | ||
.option('-s, --services <url>', 'service url, see examples, required if using -R', collect, []) | ||
.option('-t, --timeout <ms>', 'set timeout per request', parseInt) | ||
.option('-P, --parallel', 'set to parallel mode') | ||
.option('-u, --userAgent <User-Agent>', `provide a User-Agent header, default: ${defaultConfig.userAgent}`, null, '') | ||
.option('-v, --verbose', 'provide additional details') | ||
}) | ||
.parse(process.argv); | ||
const generateConfig = (cliConf) => { | ||
let config = {}; | ||
config.getIP = cliConf.parallel ? 'parallel' : 'sequential'; | ||
if (cliConf.timeout) { | ||
config.timeout = cliConf.timeout; | ||
} | ||
if (cliConf.replace) { | ||
config.replace = cliConf.replace; | ||
} | ||
if (cliConf.services.length) { | ||
config.services = cliConf.services; | ||
} | ||
if (cliConf.userAgent) { | ||
config.userAgent = cliConf.userAgent; | ||
} | ||
if (cliConf.verbose) { | ||
config.verbose = cliConf.verbose; | ||
} | ||
return config; | ||
return cliConf.options.reduce((acc, option) => { | ||
const name = option.name(); | ||
if (cliConf[name]) { | ||
acc[name] = cliConf[name]; | ||
} | ||
return acc; | ||
}, { | ||
// Patch config for parallel option. | ||
getIP: cliConf.parallel ? 'parallel' : undefined | ||
}); | ||
}; | ||
const config = generateConfig(program); | ||
const getIP = extIP(generateConfig(program)); | ||
getIP((err, ip) => { | ||
extIP(config)((err, ip) => { | ||
if (err) { | ||
console.error(err); | ||
console.error(err.message); | ||
process.exit(1); | ||
@@ -72,0 +61,0 @@ } |
'use strict'; | ||
const utils = require('./utils'); | ||
const defaultConfig = require('./defaultConfig'); | ||
module.exports = (externalConfig = {}) => { | ||
module.exports = (config = {}) => { | ||
// validate the external configuration | ||
const isValid = utils.validateConfig(externalConfig); | ||
if (isValid.errors.length) { | ||
console.error(isValid.errors); | ||
// validate the external configuration and add the default values where needed | ||
const configErrors = utils.prepareConfig(config); | ||
if (configErrors) { | ||
console.error(configErrors); | ||
process.exit(1); | ||
} | ||
// merge the external configuration with the default | ||
const config = utils.mergeConfig(externalConfig, defaultConfig); | ||
// create a request instance for each service in the configuration | ||
@@ -69,6 +64,4 @@ const requests = config.services.map((url) => utils.requestFactory(config, url)); | ||
done = true; | ||
// Abort evey pending request | ||
ongoingRequests.forEach((request) => { | ||
request.abort(); | ||
}); | ||
// Abort every pending request | ||
ongoingRequests.forEach((request) => request.abort()); | ||
return cb(null, ip); | ||
@@ -75,0 +68,0 @@ } |
'use strict'; | ||
const revalidator = require('revalidator'); | ||
const Ajv = require('ajv'); | ||
const ajv = new Ajv({ | ||
allErrors: true, | ||
useDefaults: true | ||
}); | ||
const configSchema = require('./configSchema.json'); | ||
const net = require('net'); | ||
@@ -28,56 +33,15 @@ const get = require('simple-get'); | ||
/** | ||
* Validate the configuration object using jsonschema | ||
* Prepare the configuration object. | ||
* Validate using jsonschema and apply the required defaults | ||
* @param {Object} config | ||
* @return {Object} Errors in config if present | ||
*/ | ||
const validateConfig = (config) => { | ||
return revalidator.validate(config, { | ||
properties: { | ||
replace: { | ||
description: 'true: replaces the default services, false: extends them', | ||
type: 'boolean', | ||
allowEmpty: false, | ||
dependencies: 'services' | ||
}, | ||
services: { | ||
description: 'array of urls that return the ip in the document body', | ||
type: 'array', | ||
minItems: 1, | ||
allowEmpty: false, | ||
format: 'url' | ||
}, | ||
timeout: { | ||
description: 'timeout per request', | ||
type: 'integer', | ||
allowEmpty: false | ||
}, | ||
getIP: { | ||
description: 'sequential or parallel ip fetching', | ||
type: 'string', | ||
allowEmpty: false, | ||
enum: ['parallel', 'sequential'] | ||
}, | ||
userAgent: { | ||
description: 'Customize the User-Agent header', | ||
type: 'string', | ||
allowEmpty: false | ||
} | ||
} | ||
}); | ||
}; | ||
const prepareConfig = (config) => { | ||
// Merge or extend services acordingly | ||
config.services = config.replace ? (config.services || []) : Array.isArray(config.services) ? config.services.concat(configSchema.properties.services.default) : configSchema.properties.services.default; | ||
/** | ||
* Merges the external configuration with the default | ||
* @param {Object} externalConfig | ||
* @param {Object} defaultConfig | ||
* @return {Object} | ||
*/ | ||
const mergeConfig = (externalConfig, defaultConfig) => { | ||
return { | ||
services: externalConfig.replace ? externalConfig.services : externalConfig.services && defaultConfig.services.concat(externalConfig.services) || defaultConfig.services, | ||
timeout: externalConfig.timeout || defaultConfig.timeout, | ||
getIP: externalConfig.getIP || defaultConfig.getIP, | ||
userAgent: externalConfig.userAgent || defaultConfig.userAgent, | ||
verbose: externalConfig.verbose || defaultConfig.verbose | ||
}; | ||
const validate = ajv.compile(configSchema); | ||
validate(config); | ||
return validate.errors; | ||
}; | ||
@@ -94,3 +58,6 @@ | ||
return (cb) => { | ||
logger.info(`requesting IP from: ${url}`); | ||
const startTime = Date.now(); | ||
return get.concat({ | ||
@@ -110,3 +77,3 @@ url: url, | ||
if (isIP(body)) { | ||
logger.info(`got valid IP from: ${url}`); | ||
logger.info(`got valid IP from: ${url} in ${Date.now()- startTime}ms`); | ||
return cb(null, body); | ||
@@ -137,6 +104,5 @@ } | ||
isIP, | ||
validateConfig, | ||
mergeConfig, | ||
prepareConfig, | ||
requestFactory, | ||
concatErrors | ||
}; |
{ | ||
"name": "external-ip", | ||
"version": "1.3.1", | ||
"version": "2.0.0", | ||
"description": "A node.js library to get your external ip from multiple services", | ||
@@ -37,10 +37,10 @@ "main": "index.js", | ||
"dependencies": { | ||
"commander": "^2.9.0", | ||
"revalidator": "^0.3.1", | ||
"simple-get": "^2.6.0" | ||
"ajv": "^5.5.2", | ||
"commander": "^2.15.0", | ||
"simple-get": "^2.7.0" | ||
}, | ||
"devDependencies": { | ||
"chai": "^4.0.2", | ||
"mocha": "^3.4.2" | ||
"chai": "^4.1.2", | ||
"mocha": "^3.5.3" | ||
} | ||
} |
# external-ip | ||
[![Build Status](https://travis-ci.org/J-Chaniotis/external-ip.svg?branch=master)](https://travis-ci.org/J-Chaniotis/external-ip) | ||
[![Dependency Status](https://david-dm.org/j-Chaniotis/external-ip.svg)](https://david-dm.org/j-Chaniotis/external-ip) | ||
[![npm version](https://badge.fury.io/js/external-ip.svg)](https://badge.fury.io/js/external-ip) | ||
@@ -5,0 +6,0 @@ ![XKCD 865](http://imgs.xkcd.com/comics/nanobots.png) |
@@ -16,4 +16,4 @@ 'use strict'; | ||
this.timeout(timeout); | ||
let getIP = extIP(); | ||
getIP(function (err, ip) { | ||
const getIP = extIP(); | ||
getIP((err, ip) =>{ | ||
expect(err).to.equal(null); | ||
@@ -28,3 +28,3 @@ expect(utils.isIP(ip)).to.equal(true); | ||
let getIP = extIP({ | ||
const getIP = extIP({ | ||
replace: true, // true: replace the default services list, false: extend it, default: false | ||
@@ -36,3 +36,3 @@ services: ['http://ident.me/', 'http://icanhazip.com/'], | ||
getIP(function (err, ip) { | ||
getIP((err, ip) =>{ | ||
expect(err).to.equal(null); | ||
@@ -39,0 +39,0 @@ expect(utils.isIP(ip)).to.equal(true); |
@@ -7,2 +7,3 @@ 'use strict'; | ||
const expect = require('chai').expect; | ||
const configSchema = require('../lib/configSchema.json'); | ||
@@ -32,3 +33,3 @@ | ||
timeout: 500, | ||
gerIP: 'sequential' | ||
getIP: 'sequential' | ||
}, | ||
@@ -44,11 +45,52 @@ b: { | ||
// An empty object is valid config | ||
d: {} | ||
d: {}, | ||
e: { | ||
replace: true, | ||
services: ['http://ifconfig.co/x-real-ip', 'http://ifconfig.io/ip'] | ||
}, | ||
f: { | ||
replace: true, | ||
services: ['http://ifconfig.co/x-real-ip'] | ||
}, | ||
g: { | ||
services: ['http://ifconfig.co/x-real-ip'] | ||
}, | ||
}; | ||
expect(utils.validateConfig(config.a).valid).to.equal(true); | ||
expect(utils.validateConfig(config.b).valid).to.equal(true); | ||
expect(utils.validateConfig(config.c).valid).to.equal(true); | ||
expect(utils.validateConfig(config.d).valid).to.equal(true); | ||
const defaultLength = configSchema.properties.services.default.length; | ||
expect(utils.prepareConfig(config.a)).to.equal(null); | ||
expect(config.a.services.length).to.equal(defaultLength + 2); | ||
expect(utils.prepareConfig(config.b)).to.equal(null); | ||
expect(config.b.services.length).to.equal(defaultLength + 2); | ||
expect(config.b.timeout).to.equal(500); | ||
expect(utils.prepareConfig(config.c)).to.equal(null); | ||
expect(config.c.services.length).to.equal(defaultLength); | ||
expect(config.c.timeout).to.equal(500); | ||
// Check all the default values | ||
expect(utils.prepareConfig(config.d)).to.equal(null); | ||
expect(config.d.services.length).to.equal(defaultLength); | ||
expect(config.d.replace).to.equal(configSchema.properties.replace.default); | ||
expect(config.d.timeout).to.equal(configSchema.properties.timeout.default); | ||
expect(config.d.getIP).to.equal(configSchema.properties.getIP.default); | ||
expect(config.d.userAgent).to.equal(configSchema.properties.userAgent.default); | ||
expect(config.d.verbose).to.equal(configSchema.properties.verbose.default); | ||
expect(utils.prepareConfig(config.e)).to.equal(null); | ||
expect(config.e.services.length).to.equal(2); | ||
expect(utils.prepareConfig(config.f)).to.equal(null); | ||
expect(config.f.services.length).to.equal(1); | ||
expect(utils.prepareConfig(config.g)).to.equal(null); | ||
expect(config.g.services.length).to.equal(defaultLength + 1); | ||
}); | ||
it('sould reject invalid config', function () { | ||
@@ -71,46 +113,74 @@ | ||
expect(utils.validateConfig(config.a).errors.length).equal(4); | ||
expect(utils.validateConfig(config.b).errors.length).equal(1); | ||
expect(utils.validateConfig(config.c).errors.length).equal(1); | ||
expect(utils.prepareConfig(config.a).length).equal(4); | ||
expect(utils.prepareConfig(config.b).length).equal(1); | ||
expect(utils.prepareConfig(config.c).length).equal(1); | ||
}); | ||
it('should merge a valid configuration with default configuration', function () { | ||
it('Should return an IP for every service entry in the default configuration', function (done) { | ||
const defaultServices = configSchema.properties.services.default; | ||
const config = { | ||
default: { | ||
replace: false, | ||
services: ['http://ifconfig.co/x-real-ip', 'http://ifconfig.io/ip'], | ||
timeout: 500, | ||
getIP: 'sequential' | ||
}, | ||
a: {}, | ||
b: { | ||
services: ['http://ifconfig.co/x-real-ip', 'http://ifconfig.io/ip'], | ||
timeout: 1000, | ||
getIP: 'parallel' | ||
}, | ||
c: { | ||
replace: true, | ||
services: ['http://ifconfig.co/x-real-ip'] | ||
} | ||
timeout: 1000, | ||
userAgent: 'curl/' | ||
}; | ||
// set the test timeout taking every request into account | ||
this.timeout(config.timeout * defaultServices.length); | ||
// configure a request for every service | ||
const requests = defaultServices.map((url) => utils.requestFactory(config, url)); | ||
let merged = utils.mergeConfig(config.a, config.default); | ||
expect(merged).to.have.property('timeout', 500); | ||
expect(merged).to.have.property('services').with.lengthOf(2); | ||
expect(merged).to.have.property('getIP', 'sequential'); | ||
let completed = []; | ||
// hit them and validate the results | ||
requests.forEach((request) => { | ||
request((error, ip) => { | ||
expect(error).to.equal(null); | ||
expect(utils.isIP(ip)).to.equal(true); | ||
completed.push(ip); | ||
// Every service has responded | ||
if (completed.length === requests.length) { | ||
merged = utils.mergeConfig(config.b, config.default); | ||
expect(merged).to.have.property('timeout', 1000); | ||
expect(merged).to.have.property('services').with.lengthOf(4); | ||
expect(merged).to.have.property('getIP', 'parallel'); | ||
// When runing on travis IPs will not be the same because of the infrastructure | ||
if(process.env.TRAVIS) { | ||
return done(); | ||
} | ||
// Check if every IP is the same | ||
return (!!completed.reduce((a, b) => a === b ? a : NaN)) ? done() : done(new Error('IP mismatch')); | ||
} | ||
}); | ||
}); | ||
merged = utils.mergeConfig(config.c, config.default ); | ||
expect(merged).to.have.property('timeout', 500); | ||
expect(merged).to.have.property('services').with.lengthOf(1); | ||
}); | ||
it('should be able to fail as expected when an service cant be found', function (done) { | ||
const config = { | ||
timeout: 1000, | ||
userAgent: 'curl/' | ||
}; | ||
utils.requestFactory(config, 'http://i am doomed to fail cause i dont exist')((error, ip) => { | ||
expect(ip).to.equal(null); | ||
expect(error.message).to.contain('ENOTFOUND'); | ||
done(); | ||
}); | ||
}); | ||
it('should be able to fail as expected when an service wont return a valid IP', function (done) { | ||
const config = { | ||
timeout: 1000, | ||
userAgent: 'curl/' | ||
}; | ||
utils.requestFactory(config, 'http://www.google.com')((error, ip) => { | ||
expect(ip).to.equal(null); | ||
expect(error.message).to.contain('invalid IP'); | ||
done(); | ||
}); | ||
}); | ||
it('should be able to format multiple error messages', function () { | ||
const error = utils.concatErrors([ | ||
new Error('Software failure.'), | ||
new Error('Press left mouse button to continue'), | ||
new Error('Guru Meditation '), | ||
]); | ||
expect(error.message).to.equal('Multiple errors: \n Software failure. \n Press left mouse button to continue \n Guru Meditation \n'); | ||
}); | ||
}); |
Sorry, the diff of this file is not supported yet
NPM Shrinkwrap
Supply chain riskPackage contains a shrinkwrap file. This may allow the package to bypass normal install procedures.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
37793
868
159
16
1
1
2
+ Addedajv@^5.5.2
+ Addedajv@5.5.2(transitive)
+ Addedco@4.6.0(transitive)
+ Addedfast-deep-equal@1.1.0(transitive)
+ Addedfast-json-stable-stringify@2.1.0(transitive)
+ Addedjson-schema-traverse@0.3.1(transitive)
- Removedrevalidator@^0.3.1
- Removedrevalidator@0.3.1(transitive)
Updatedcommander@^2.15.0
Updatedsimple-get@^2.7.0