├── .gitignore
├── LICENSE.md
├── README.md
├── _error
├── 404.css
├── 404.html
├── geoip32.png
└── geoip48.png
├── _lib
├── lib.js
├── package-lock.json
├── package.json
└── stats.js
├── _public
├── icons
│ ├── geoip128.png
│ ├── geoip32.png
│ ├── geoip48.png
│ └── geoip64.png
└── sprites
│ └── sprites.png
├── client
├── index.css
├── index.html
├── index.js
├── robots.txt
└── sitemap.xml
└── server
├── geoip.js
├── geoipTask.js
├── package-lock.json
├── package.json
└── test
└── geoip_test.js
/.gitignore:
--------------------------------------------------------------------------------
1 | #Ignore these directories
2 | _aux/
3 | node_modules/
4 | tmp/
5 |
6 | #ignore these files
7 | # Ignore Mac DS_Store files
8 | .env
9 | .DS_Store
10 | config.js
11 | config.json
12 | private.json
13 | secret.json
14 | stats.json
15 |
16 | # ip databases
17 | _lib/db
18 | _lib/csv
19 | GeoLite2-City.mmdb
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | BSD 3-Clause License
2 |
3 | Copyright (c) 2017, Jolav All rights reserved.
4 |
5 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
6 |
7 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
8 |
9 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
10 |
11 | * Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
12 |
13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | 
3 | 
4 |
5 | #  **[GEOIP-TOOLS](https://geoip.tools)**
6 |
7 |
8 | Free GeoIP service that provides a public HTTPS (SSL access) API to retrieve geolocation information from any IPv4, IPv6 or hostname.
9 |
10 | version 0.2.5
11 |
12 | ### **[API DOCS](https://geoip.tools)**
13 |
14 | HTTP Request Template:
15 | `GET https://geoip.tools/v1/{format}/?q={IP-or-hostname}`
16 |
17 | Supported formats : json , xml and csv
18 | If no IP or hostname is provided it retrieves your own IP
19 | IPv4 and IPv6 supported
20 | CORS support out of the box makes geoip.tools perfect to your front end apps
21 |
22 |
23 | * **Examples:**
24 |
25 | [https://geoip.tools/v1/json](https://geoip.tools/v1/json)
26 |
27 |
28 | [https://geoip.tools/v1/json/?q=geoip.tools](https://geoip.tools/v1/json/?q=geoip.tools)
29 |
30 |
31 | [https://geoip.tools/v1/xml/?q=192.168.200.200](https://geoip.tools/v1/xml/?q=192.168.200.200)
32 |
33 | [https://geoip.tools/v1/xml/?q=2a00:1450:4006:803::200e](https://geoip.tools/v1/xml/?q=2a00:1450:4006:803::200e)
34 |
35 |
36 | * **Usage Limits:**
37 |
38 | 300 requests per minute (432.000 API requests daily). Once reached subsequent requests will result in error 503 until your quota is cleared.
39 | If you need more quota contact us.
40 | Our API requires no key or signup.
41 |
42 | * **Example**
43 |
44 | ```json
45 | {
46 | "ip": "192.168.200.200",
47 | "country_code": "GB",
48 | "country_name": "United Kingdom",
49 | "region_code": "ENG",
50 | "region_name": "England",
51 | "city": "London",
52 | "zip_code": "SL1",
53 | "time_zone": "Europe/London",
54 | "latitude": 50.0500,
55 | "longitude": 0.6172
56 | }
57 | ```
58 |
59 |
60 |
61 | ## **Acknowledgment**
62 |
63 | * This site includes GeoLite2 data created by MaxMind, available from [maxmind.com](http://maxmind.com)
64 |
--------------------------------------------------------------------------------
/_error/404.css:
--------------------------------------------------------------------------------
1 | body {
2 | font-family: 'arial', sans-serif;
3 | background-color: #fafafa;
4 | font-size: 1em;
5 | margin: auto;
6 | max-width: 960px;
7 | -webkit-user-select: none;
8 | /* Chrome all / Safari all */
9 | -moz-user-select: none;
10 | /* Firefox all */
11 | -ms-user-select: none;
12 | /* IE 10+ */
13 | user-select: none;
14 | color: #404040;
15 | }
16 |
17 | @media (max-width: 540px) {
18 | .error {
19 | flex-direction: column;
20 | align-items: center;
21 | }
22 | .type {
23 | margin-top: 20px;
24 | }
25 | }
26 |
27 | @media (min-width: 541px) {
28 | .error {
29 | align-items: baseline;
30 | }
31 | .number {
32 | padding-right: 20px;
33 | }
34 | .type {
35 | padding-left: 20px;
36 | }
37 | }
38 |
39 | .error {
40 | display: flex;
41 | }
42 |
43 | .container {
44 | margin-top: 50px;
45 | display: flex;
46 | flex-flow: column nowrap;
47 | align-items: center;
48 | }
49 |
50 | .number {
51 | font-size: 5em;
52 | font-weight: bold;
53 | display: flex;
54 | }
55 |
56 | .digit {
57 | border: 1px solid #404040;
58 | padding: 5px 10px;
59 | }
60 |
61 | .digit + .digit {
62 | border-left: none;
63 | }
64 |
65 | .type {
66 | font-size: 2em;
67 | }
68 |
69 | .explanation {
70 | margin-top: 30px;
71 | }
72 |
73 | .options {
74 | font-size: 1.2em;
75 | margin-top: 10px;
76 | }
77 |
78 | a.link {
79 | color: #2cc0e9;
80 | /* color: #375EAB;*/
81 | cursor: pointer;
82 | text-decoration: none;
83 | }
84 |
85 | a.link:hover {
86 | color: lightskyblue;
87 | }
88 |
89 |
90 | /* TITLE */
91 |
92 | .titleBox {
93 | padding: 0px;
94 | font-size: 2.5em;
95 | text-align: center;
96 | color: #e9552c;
97 | }
98 |
99 | .titleBox img {
100 | vertical-align: middle;
101 | max-height: 48px;
102 | padding: 0px 15px 8px 0px;
103 | }
--------------------------------------------------------------------------------
/_error/404.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | 404 Page Not Found
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | geoIP.tools
26 |
27 |
28 |
29 |
30 | 4
31 | 0
32 | 4
33 |
34 |
35 | Page Not Found
36 |
37 |
38 |
39 |
40 | We can't find the page you are looking for.
41 |
42 |
46 |
47 |
48 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/_error/geoip32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/apilayer/geoip-tools/ab5fdbdfac0202cf9645109365dc0248b1948a11/_error/geoip32.png
--------------------------------------------------------------------------------
/_error/geoip48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/apilayer/geoip-tools/ab5fdbdfac0202cf9645109365dc0248b1948a11/_error/geoip48.png
--------------------------------------------------------------------------------
/_lib/lib.js:
--------------------------------------------------------------------------------
1 | /* */
2 |
3 | function getIP (req) {
4 | return (req.headers['x-forwarded-for'] ||
5 | req.connection.remoteAddress || req.socket.remoteAddress ||
6 | req.connection.socket.remoteAddress).split(',')[0];
7 | }
8 |
9 | function isValidIP (ip) {
10 | if (/^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/.test(ip)) {
11 | return true;
12 | } else if (/(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))/.test(ip)) {
13 | return true;
14 | } else {
15 | return false;
16 | }
17 | }
18 |
19 | function isValidHostname (hostname) {
20 | let condition = /^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\\-]*[A-Za-z0-9])$/;
21 | if (condition.test(hostname)) {
22 | return true;
23 | }
24 | return false;
25 | }
26 |
27 | function addDays (date, days) {
28 | var result = new Date(date);
29 | result.setDate(result.getDate() + days);
30 | return result;
31 | }
32 |
33 | module.exports = {
34 | getIP: getIP,
35 | isValidIP: isValidIP,
36 | isValidHostname: isValidHostname,
37 | addDays: addDays
38 | };
39 |
--------------------------------------------------------------------------------
/_lib/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "_lib",
3 | "version": "0.0.1",
4 | "lockfileVersion": 1,
5 | "requires": true,
6 | "dependencies": {
7 | "big-integer": {
8 | "version": "1.6.32",
9 | "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.32.tgz",
10 | "integrity": "sha512-ljKJdR3wk9thHfLj4DtrNiOSTxvGFaMjWrG4pW75juXC4j7+XuKJVFdg4kgFMYp85PVkO05dFMj2dk2xVsH4xw=="
11 | },
12 | "bignumber.js": {
13 | "version": "4.1.0",
14 | "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-4.1.0.tgz",
15 | "integrity": "sha512-eJzYkFYy9L4JzXsbymsFn3p54D+llV27oTQ+ziJG7WFRheJcNZilgVXMG0LoZtlQSKBsJdWtLFqOD0u+U0jZKA=="
16 | },
17 | "core-util-is": {
18 | "version": "1.0.2",
19 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
20 | "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
21 | },
22 | "dotenv": {
23 | "version": "6.0.0",
24 | "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-6.0.0.tgz",
25 | "integrity": "sha512-FlWbnhgjtwD+uNLUGHbMykMOYQaTivdHEmYwAKFjn6GKe/CqY0fNae93ZHTd20snh9ZLr8mTzIL9m0APQ1pjQg=="
26 | },
27 | "inherits": {
28 | "version": "2.0.3",
29 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
30 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
31 | },
32 | "isarray": {
33 | "version": "1.0.0",
34 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
35 | "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
36 | },
37 | "maxmind": {
38 | "version": "2.7.0",
39 | "resolved": "https://registry.npmjs.org/maxmind/-/maxmind-2.7.0.tgz",
40 | "integrity": "sha512-blmBnRbEkYBGz+7kxNQzuYUwyMyJ5PYGhrMYe6RXmMSBxZXe2665INgNP5/8WuYdWnDJ1Z8osC/LVKdsbT2fnQ==",
41 | "requires": {
42 | "big-integer": "^1.6.31",
43 | "tiny-lru": "^1.6.1"
44 | }
45 | },
46 | "mysql": {
47 | "version": "2.16.0",
48 | "resolved": "https://registry.npmjs.org/mysql/-/mysql-2.16.0.tgz",
49 | "integrity": "sha512-dPbN2LHonQp7D5ja5DJXNbCLe/HRdu+f3v61aguzNRQIrmZLOeRoymBYyeThrR6ug+FqzDL95Gc9maqZUJS+Gw==",
50 | "requires": {
51 | "bignumber.js": "4.1.0",
52 | "readable-stream": "2.3.6",
53 | "safe-buffer": "5.1.2",
54 | "sqlstring": "2.3.1"
55 | }
56 | },
57 | "process-nextick-args": {
58 | "version": "2.0.0",
59 | "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz",
60 | "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw=="
61 | },
62 | "readable-stream": {
63 | "version": "2.3.6",
64 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
65 | "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
66 | "requires": {
67 | "core-util-is": "~1.0.0",
68 | "inherits": "~2.0.3",
69 | "isarray": "~1.0.0",
70 | "process-nextick-args": "~2.0.0",
71 | "safe-buffer": "~5.1.1",
72 | "string_decoder": "~1.1.1",
73 | "util-deprecate": "~1.0.1"
74 | }
75 | },
76 | "safe-buffer": {
77 | "version": "5.1.2",
78 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
79 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
80 | },
81 | "sqlstring": {
82 | "version": "2.3.1",
83 | "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.1.tgz",
84 | "integrity": "sha1-R1OT/56RR5rqYtyvDKPRSYOn+0A="
85 | },
86 | "string_decoder": {
87 | "version": "1.1.1",
88 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
89 | "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
90 | "requires": {
91 | "safe-buffer": "~5.1.0"
92 | }
93 | },
94 | "tiny-lru": {
95 | "version": "1.6.1",
96 | "resolved": "https://registry.npmjs.org/tiny-lru/-/tiny-lru-1.6.1.tgz",
97 | "integrity": "sha512-m8oyPnHjnQlbDk8+MCw33qRMp6+BxPxoayN9C743VToeyQ5zZV6F6vkklrYVEI0z9MQ3+jmc+22tKmvPg4gmoA=="
98 | },
99 | "util-deprecate": {
100 | "version": "1.0.2",
101 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
102 | "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
103 | }
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/_lib/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "_lib",
3 | "version": "0.0.1",
4 | "description": "",
5 | "main": "lib.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "keywords": [],
10 | "author": "jolav",
11 | "license": "BSD-3-Clause",
12 | "dependencies": {
13 | "dotenv": "^6.0.0",
14 | "maxmind": "^2.7.0",
15 | "mysql": "^2.16.0"
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/_lib/stats.js:
--------------------------------------------------------------------------------
1 | /* */
2 | require('dotenv').config();
3 | const lib = require('./lib.js');
4 |
5 | const TABLE = process.env.TABLE;
6 |
7 | const mysql = require('mysql');
8 | const con = mysql.createConnection({
9 | host: process.env.HOST,
10 | user: process.env.MYSQLUSER,
11 | password: process.env.PASSWORD,
12 | database: process.env.DB_GEOIP
13 | });
14 |
15 | function updateStats (req, res, next) {
16 | const test = lib.getIP(req);
17 | if (test) {
18 | const time = new Date().toISOString().split('T')[0];
19 | if (process.env.NODE_ENV === 'production') {
20 | insertHit(time);
21 | } else {
22 | console.log('SAVE TEST ...');
23 | }
24 | } else {
25 | console.log(test, 'DONT SAVE => ');
26 | }
27 | next();
28 | }
29 |
30 | function insertHit (time) {
31 | let sql = 'INSERT INTO ?? (day, geoip)';
32 | sql += ' VALUES (?, 0)';
33 | sql += ` ON DUPLICATE KEY UPDATE geoip = geoip + 1;`;
34 | const inserts = [TABLE, time];
35 | sql = mysql.format(sql, inserts);
36 | con.query(sql, function (err, rows) {
37 | if (err) {
38 | console.log('Insert HIT error =>', err);
39 | // throw err
40 | } else {
41 | // console.log(rows)
42 | }
43 | });
44 | }
45 |
46 | function testDB () {
47 | console.log('Connecting ......');
48 | // console.log(con)
49 | con.connect(function (err) {
50 | if (err) {
51 | console.log('Error connecting to DB => ', err);
52 | } else {
53 | console.log('Connection OK');
54 | }
55 | });
56 | }
57 |
58 | module.exports = {
59 | updateStats: updateStats,
60 | testDB: testDB
61 | };
62 |
--------------------------------------------------------------------------------
/_public/icons/geoip128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/apilayer/geoip-tools/ab5fdbdfac0202cf9645109365dc0248b1948a11/_public/icons/geoip128.png
--------------------------------------------------------------------------------
/_public/icons/geoip32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/apilayer/geoip-tools/ab5fdbdfac0202cf9645109365dc0248b1948a11/_public/icons/geoip32.png
--------------------------------------------------------------------------------
/_public/icons/geoip48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/apilayer/geoip-tools/ab5fdbdfac0202cf9645109365dc0248b1948a11/_public/icons/geoip48.png
--------------------------------------------------------------------------------
/_public/icons/geoip64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/apilayer/geoip-tools/ab5fdbdfac0202cf9645109365dc0248b1948a11/_public/icons/geoip64.png
--------------------------------------------------------------------------------
/_public/sprites/sprites.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/apilayer/geoip-tools/ab5fdbdfac0202cf9645109365dc0248b1948a11/_public/sprites/sprites.png
--------------------------------------------------------------------------------
/client/index.css:
--------------------------------------------------------------------------------
1 | /* BIG SMALL */
2 |
3 | @media only screen and (max-width: 681px) {
4 | .big {
5 | display: none
6 | }
7 | .small {
8 | display: block;
9 | }
10 | h1 {
11 | font-size: 2em;
12 | }
13 | }
14 |
15 | @media only screen and (min-width: 681px) {
16 | .big {
17 | display: block
18 | }
19 | .small {
20 | display: none;
21 | }
22 | h1 {
23 | font-size: 3em;
24 | }
25 | }
26 |
27 | @media only screen and (max-width: 681px) {
28 | body {
29 | padding: 5px;
30 | }
31 | .data {
32 | display: flex;
33 | flex-flow: column wrap;
34 | align-items: center;
35 | }
36 | .data1 {
37 | width: 80%;
38 | max-width: 400px;
39 | margin-bottom: 10px;
40 | }
41 | .data2 {
42 | width: 80%;
43 | max-width: 400px;
44 | }
45 | }
46 |
47 | @media only screen and (min-width: 681px) {
48 | body {
49 | padding: 10px;
50 | }
51 | .data {
52 | display: flex;
53 | flex-flow: row wrap;
54 | align-items: center;
55 | }
56 | .data1 {
57 | flex: 3;
58 | margin-right: 15px
59 | }
60 | .data2 {
61 | flex: 2;
62 | margin-left: 15px;
63 | }
64 | }
65 |
66 | body {
67 | font-family: "Varela", "arial", sans-serif;
68 | background-color: #eee;
69 | color: #404040;
70 | font-size: 1em;
71 | margin: auto;
72 | max-width: 780px;
73 | -webkit-user-select: none;
74 | /* Chrome all / Safari all */
75 | -moz-user-select: none;
76 | /* Firefox all */
77 | -ms-user-select: none;
78 | /* IE 10+ */
79 | user-select: none;
80 | }
81 |
82 | /* GENERICS */
83 |
84 | .centerImage {
85 | display: flex;
86 | margin: 0 auto;
87 | max-width: 60%;
88 | }
89 |
90 | .verticalImage {
91 | vertical-align: middle;
92 | }
93 |
94 | .centerText {
95 | text-align: center;
96 | }
97 |
98 | /* HEADER */
99 |
100 | .header {
101 | margin-bottom: 10px;
102 | }
103 |
104 | h1 {
105 | margin: 0px;
106 | color: #e9552c;
107 | }
108 |
109 | /* DATA = RESULTS + MAP */
110 |
111 | .data {}
112 |
113 | .data1 {
114 | padding: 10px;
115 | background-color: lightblue;
116 | border-radius: 10px;
117 | }
118 |
119 | .form input[type=text] {
120 | display: flex;
121 | margin: 0px auto 10px auto;
122 | width: 80%;
123 | text-align: center;
124 | font-size: 1.3em;
125 | border: none;
126 | border-bottom: 2px solid #e9552c;
127 | background-color: #eee;
128 | }
129 |
130 | .table {
131 | width: 100%;
132 | }
133 |
134 | tr.row td.key, tr.row td.value {
135 | border-bottom: 1px solid #bbbbbb;
136 | }
137 |
138 | tr.row td.key {
139 | width: 35%;
140 | }
141 |
142 | tr.row td.value {
143 | width: 65%;
144 | }
145 |
146 | .key {
147 | text-align: left;
148 | font-size: 0.8em;
149 | }
150 |
151 | .value {
152 | font-size: 0.95em;
153 | }
154 |
155 | .data2 {
156 | /* background-color: lightcoral;*/
157 | }
158 |
159 | .map {
160 | padding: 10px;
161 | width: 90%;
162 | margin: 0px auto;
163 | }
164 |
165 | .map #mapid {
166 | height: 220px;
167 | }
168 |
169 | @media only screen and (max-width: 681px) {
170 | .docs {
171 | flex-flow: column wrap;
172 | }
173 | .docs1 {
174 | width: 90%;
175 | }
176 | .docs2 {
177 | margin-top: 15px;
178 | }
179 | }
180 |
181 | @media only screen and (min-width: 681px) {
182 | .doc {
183 | flex-flow: row nowrap;
184 | }
185 | .docs1 {
186 | margin-right: 30px;
187 | }
188 | }
189 |
190 | /* DOCS */
191 |
192 | .docs {
193 | font-size: 0.9em;
194 | margin-top: 10px;
195 | display: flex;
196 | justify-content: space-around;
197 | align-items: center;
198 | }
199 |
200 | code, pre {
201 | font-size: 1.15em;
202 | }
203 |
204 | .red {
205 | color: #e9552c;
206 | }
207 |
208 | /* HR */
209 |
210 | hr {
211 | margin: 0px 10%;
212 | border: 0;
213 | height: 1px;
214 | background: #333;
215 | background-image: linear-gradient(to right, #ccc, #888, #ccc);
216 | }
217 |
218 | /* FOOTER */
219 |
220 | .footer {
221 | margin-top: 10px;
222 | text-align: center;
223 | }
224 |
225 | a.hard {
226 | color: #375EAB;
227 | }
228 |
229 | a.newSite {
230 | color: #375EAB;
231 | }
232 |
233 | a {
234 | color: #2cc0e9;
235 | text-decoration: none;
236 | }
237 |
238 | /* Generated by http://css.spritegen.com CSS Sprite Generator */
239 |
240 | .geoip128, .geoip64 {
241 | display: inline-block;
242 | background: url('/_public/sprites/sprites.png') no-repeat;
243 | overflow: hidden;
244 | text-indent: -9999px;
245 | text-align: left;
246 | }
247 |
248 | .geoip128 {
249 | background-position: -0px -0px;
250 | width: 128px;
251 | height: 128px;
252 | }
253 |
254 | .geoip64 {
255 | background-position: -0px -128px;
256 | width: 64px;
257 | height: 64px;
258 | }
--------------------------------------------------------------------------------
/client/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | geoIP.tools
7 |
8 |
9 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
39 |
40 |
41 |
42 |
45 |
46 |
47 | IP
48 |
49 |
50 |
51 | City
52 |
53 |
54 |
55 | Region/Code
56 |
57 |
58 |
59 | Country/Code
60 |
61 |
62 |
63 | Zip Code
64 |
65 |
66 |
67 | Time Zone
68 |
69 |
70 |
71 | Lat/Lon
72 |
73 |
74 |
75 |
76 |
81 |
82 |
83 |
84 |
85 | API doc
86 |
87 | (version 0.2.5)
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 | HTTP Request template:
96 |
97 |
98 |
GET
99 | https://geoip.tools/v1/{format}/?q={IP-or-hostname}
100 |
101 |
102 |
Supported formats :
103 | json ,
104 | xml
105 | and
106 |
csv
107 |
108 |
If no IP or hostname is provided it retrieves your own IP
109 |
110 |
IPv4 and IPv6 supported
111 |
112 |
CORS support out of the box makes geoip.tools perfect to your front end apps or webs
113 |
114 |
115 |
116 | Examples:
117 |
118 |
119 |
- https://geoip.tools/v1/json
120 |
121 |
- https://geoip.tools/v1/json/?q=geoip.tools
122 |
123 |
- https://geoip.tools/v1/xml/?q=192.168.200.200
124 |
125 |
- https://geoip.tools/v1/xml/?q=2a00:1450:4006:803::200e
126 |
127 |
128 |
129 | Usage Limits:
130 |
131 |
132 |
300 requests per minute (432.000 API requests daily) Once reached subsequent requests will result in error 503 until
133 | your quota is cleared
134 |
135 |
If you need more quota contact us
136 |
137 |
Our API requires no key or signup.
138 |
139 |
140 |
141 |
142 |
143 |
144 | JSON response example
145 |
146 |
147 | {
148 | "ip": "192.168.200.200",
149 | "country_code": "GB",
150 | "country_name": "United Kingdom",
151 | "region_code": "ENG",
152 | "region_name": "England",
153 | "city": "London",
154 | "zip_code": "SL1",
155 | "time_zone": "Europe/London",
156 | "latitude": 50.0500,
157 | "longitude": 0.6172
158 | }
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
197 |
198 |
199 |
212 |
213 |
214 |
215 |
217 |
218 |
219 |
221 |
222 |
223 |
224 |
225 |
--------------------------------------------------------------------------------
/client/index.js:
--------------------------------------------------------------------------------
1 | /* global L value1 value2 value3 value4 value5 value6 value7 */
2 | const geoip = (function () {
3 | 'use strict';
4 | /* code here */
5 |
6 | // let geoipUrl = 'http://localhost:3000/json'
7 | let geoipUrl = 'https://geoip.tools/v1/json';
8 | let q = '';
9 | let data = {};
10 |
11 | function init () {
12 | q = getURLParameter('q');
13 | if (q === '' || q === null || q === undefined) {
14 | getAjaxData(geoipUrl, drawMap);
15 | } else {
16 | // console.log(geoipUrl + '?q=' + q)
17 | getAjaxData(geoipUrl + '?q=' + q, drawMap);
18 | }
19 | }
20 |
21 | function drawMap (data) {
22 | console.log(data);
23 | if (q === '' || q === null || q === undefined) {
24 | document.getElementById('q').value = data.ip;
25 | } else {
26 | document.getElementById('q').value = q;
27 | }
28 | value1.innerText = data.ip;
29 | value2.innerText = data.city;
30 | value3.innerText = data.region_name + ' ' + data.region_code;
31 | value4.innerText = data.country_name + ' ' + data.country_code;
32 | value5.innerText = data.zip_code;
33 | value6.innerText = data.time_zone;
34 | value7.innerText = data.latitude + ' , ' + data.longitude;
35 |
36 | let map = L.map('mapid').setView([data.latitude, data.longitude], 4);
37 | let popupText = `You Are Here IP : ${data.ip}`;
38 | L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
39 | attribution: '© OpenStreetMap contributors'
40 | }).addTo(map);
41 | L.marker([data.latitude, data.longitude])
42 | .addTo(map)
43 | .bindPopup(popupText)
44 | .openPopup();
45 | }
46 |
47 | function getAjaxData (urlData, callback) {
48 | const xhr = new XMLHttpRequest();
49 | xhr.onreadystatechange = function () {
50 | if (xhr.readyState === 4) { // 4 = "DONE"
51 | if (xhr.status === 200) { // 200 ="OK"
52 | callback(JSON.parse(xhr.responseText));
53 | } else {
54 | console.log('Error: ' + xhr.status);
55 | }
56 | }
57 | };
58 | xhr.open('GET', urlData); // add false to synchronous request
59 | xhr.send();
60 | }
61 |
62 | function getURLParameter (name) {
63 | return decodeURIComponent((new RegExp('[?|&]' + name + '=' + '([^&;]+?)(&|#|;|$)').exec(location.search) || [null, ''])[1].replace(/\+/g, '%20')) || null;
64 | }
65 |
66 | return {
67 | init: init
68 | };
69 | }());
70 |
71 | window.addEventListener('load', geoip.init);
72 |
--------------------------------------------------------------------------------
/client/robots.txt:
--------------------------------------------------------------------------------
1 | User-Agent: *
2 | Allow: /
3 |
4 |
--------------------------------------------------------------------------------
/client/sitemap.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | http://geoip.tools/ daily 1.00
4 | http://geoip.tools/ daily 1.00
5 | https://geoip.tools daily 1.00
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/server/geoip.js:
--------------------------------------------------------------------------------
1 | /* */
2 |
3 | const express = require('express');
4 | const app = express();
5 |
6 | require('dotenv').config({ path: __dirname + '/../_lib/.env' });
7 | const job = require(__dirname + '/geoipTask.js');
8 | const stats = require(__dirname + '/../_lib/stats.js');
9 |
10 | let port = 3000;
11 | if (process.env.NODE_ENV === 'production') {
12 | port = process.env.PORT_GEOIP;
13 | }
14 |
15 | app.disable('x-powered-by');
16 |
17 | app.use(function (req, res, next) {
18 | stats.updateStats(req, res, next);
19 | });
20 |
21 | app.get('/json/', (req, res) => {
22 | job.getJson(req, res, 'json');
23 | });
24 |
25 | app.get('/xml/', (req, res) => {
26 | job.getJson(req, res, 'xml');
27 | });
28 |
29 | app.get('/csv/', (req, res) => {
30 | job.getJson(req, res, 'csv');
31 | });
32 |
33 | app.get('*', (req, res) => {
34 | res.redirect('https://geoip.tools/notFound');
35 | // res.status(404).send('Not Found')
36 | });
37 |
38 | app.listen(port, function () {
39 | const time = new Date().toUTCString().split(',')[1];
40 | console.log('Express server on port ' + port + ' - ' + time);
41 | });
42 |
43 | module.exports = app;
44 |
--------------------------------------------------------------------------------
/server/geoipTask.js:
--------------------------------------------------------------------------------
1 | /* */
2 | const dns = require('dns');
3 | const maxmind = require('maxmind');
4 | const json2xml = require('js2xmlparser');
5 | const json2csv = require('json2csv');
6 | const fs = require('fs');
7 | const lib = require(__dirname + '/../_lib/lib.js');
8 | const path = require('path');
9 |
10 | /* DATABASE IN MEMORY */
11 | const MMDBReader = require('mmdb-reader');
12 | const dbpath = path.join(__dirname , '/../_lib/db/GeoLite2-City.mmdb');
13 | const reader = new MMDBReader(dbpath);
14 |
15 | let data;
16 | let geoData;
17 |
18 | function getJson (req, res, format) {
19 | cleanData();
20 | let q = req.query.q;
21 | if (!q) {
22 | q = lib.getIP(req);
23 | }
24 | if (lib.isValidIP(q)) {
25 | getDataFromRAM(req, res, format, q);
26 | return;
27 | }
28 | if (lib.isValidHostname(q)) {
29 | dns.lookup(q, function (err, ip) {
30 | if (err) {
31 | let text = `${q} is a unknown host, not a valid IP or hostname`;
32 | sendResult(res, 'json', text, 400);
33 | return;
34 | }
35 | if (ip === null || ip === undefined) {
36 | let text = `${q} is a unknown host, not a valid IP or hostname`;
37 | sendResult(res, 'json', text, 400);
38 | return;
39 | }
40 | getDataFromRAM(req, res, format, ip);
41 | return;
42 | });
43 | return;
44 | }
45 | let text = `${q} is a unknown host, not a valid IP or hostname`;
46 | sendResult(res, 'json', text, 400);
47 | }
48 |
49 | function getDataFromDB (req, res, format, ip) {
50 | const dbpath = path.join(__dirname , '/../_lib/db/GeoLite2-City.mmdb');
51 | maxmind.open(dbpath, function (err, cityLookup) {
52 | if (err) {
53 | console.log('Error => ', err);
54 | let text = 'Error accessing database. Try again';
55 | sendResult(res, 'json', text, 500);
56 | return;
57 | }
58 | data = cityLookup.get(ip);
59 | if (data === null) { // not in database
60 | sendResult(res, format, geoData, 200);
61 | return;
62 | }
63 | geoData = handleGeoData(ip, data);
64 | sendResult(res, format, geoData, 200);
65 | return;
66 | });
67 | }
68 |
69 | function getDataFromRAM (req, res, format, ip) {
70 | data = reader.lookup(ip);
71 | if (data === null) { // not in database
72 | sendResult(res, format, geoData, 200);
73 | return;
74 | }
75 | geoData = handleGeoData(ip, data);
76 | sendResult(res, format, geoData, 200);
77 | return;
78 | }
79 |
80 | function sendResult (res, format, data, status) {
81 | if (format === 'json') {
82 | res.header('Content-Type', 'application/json');
83 | res.status(status).send(JSON.stringify(data, null, 3));
84 | } else if (format === 'xml') {
85 | res.header('Content-Type', 'text/xml');
86 | res.status(status).send(json2xml.parse('myGeoData', data));
87 | } else if (format === 'csv') {
88 | let fields = [
89 | 'ip',
90 | 'country_code',
91 | 'country_name',
92 | 'region_code',
93 | 'region_name',
94 | 'city',
95 | 'zip_code',
96 | 'time_zone',
97 | 'latitude',
98 | 'longitude'];
99 | try {
100 | let fileName = data.ip + '.csv';
101 | let result = json2csv({ data: data, fields: fields });
102 | fs.writeFile(fileName, result, function (err) {
103 | if (err) {
104 | throw err;
105 | }
106 | res.download(fileName, fileName, function (err) {
107 | if (err) {
108 | res.status(404).end();
109 | return;
110 | }
111 | fs.unlink(fileName, undefined);
112 | });
113 | });
114 | } catch (err) {
115 | res.end();
116 | }
117 | }
118 | cleanData();
119 | return;
120 | }
121 |
122 | function handleGeoData (ip, data) {
123 | geoData.ip = ip;
124 | if (data.country !== undefined) {
125 | geoData.country_code = data.country.iso_code || '';
126 | geoData.country_name = data.country.names['en'] || '';
127 | }
128 | if (data.subdivisions !== undefined) {
129 | geoData.region_code = data.subdivisions[0].iso_code || '';
130 | geoData.region_name = data.subdivisions[0].names['en'] || '';
131 | }
132 | if (data.city !== undefined) {
133 | geoData.city = data.city.names['en'] || '';
134 | }
135 | if (data.postal !== undefined) {
136 | geoData.zip_code = data.postal.code || '';
137 | }
138 | if (data.location !== undefined) {
139 | geoData.time_zone = data.location.time_zone || '';
140 | geoData.latitude = data.location.latitude.toFixed(4) || '';
141 | geoData.longitude = data.location.longitude.toFixed(4) || '';
142 | }
143 | return geoData;
144 | }
145 |
146 | function cleanData () {
147 | data = {};
148 | geoData = {
149 | 'ip': '',
150 | 'country_code': '',
151 | 'country_name': '',
152 | 'region_code': '',
153 | 'region_name': '',
154 | 'city': '',
155 | 'zip_code': '',
156 | 'time_zone': '',
157 | 'latitude': '',
158 | 'longitude': ''
159 | };
160 | }
161 |
162 | module.exports = {
163 | getJson: getJson
164 | };
165 |
--------------------------------------------------------------------------------
/server/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "geoip",
3 | "version": "0.2.3",
4 | "lockfileVersion": 1,
5 | "requires": true,
6 | "dependencies": {
7 | "accepts": {
8 | "version": "1.3.5",
9 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz",
10 | "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=",
11 | "requires": {
12 | "mime-types": "~2.1.18",
13 | "negotiator": "0.6.1"
14 | }
15 | },
16 | "array-flatten": {
17 | "version": "1.1.1",
18 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
19 | "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
20 | },
21 | "assertion-error": {
22 | "version": "1.1.0",
23 | "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz",
24 | "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==",
25 | "dev": true
26 | },
27 | "asynckit": {
28 | "version": "0.4.0",
29 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
30 | "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=",
31 | "dev": true
32 | },
33 | "big-integer": {
34 | "version": "1.6.32",
35 | "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.32.tgz",
36 | "integrity": "sha512-ljKJdR3wk9thHfLj4DtrNiOSTxvGFaMjWrG4pW75juXC4j7+XuKJVFdg4kgFMYp85PVkO05dFMj2dk2xVsH4xw=="
37 | },
38 | "body-parser": {
39 | "version": "1.18.2",
40 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.2.tgz",
41 | "integrity": "sha1-h2eKGdhLR9hZuDGZvVm84iKxBFQ=",
42 | "requires": {
43 | "bytes": "3.0.0",
44 | "content-type": "~1.0.4",
45 | "debug": "2.6.9",
46 | "depd": "~1.1.1",
47 | "http-errors": "~1.6.2",
48 | "iconv-lite": "0.4.19",
49 | "on-finished": "~2.3.0",
50 | "qs": "6.5.1",
51 | "raw-body": "2.3.2",
52 | "type-is": "~1.6.15"
53 | }
54 | },
55 | "bytes": {
56 | "version": "3.0.0",
57 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
58 | "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg="
59 | },
60 | "chai": {
61 | "version": "4.1.2",
62 | "resolved": "https://registry.npmjs.org/chai/-/chai-4.1.2.tgz",
63 | "integrity": "sha1-D2RYS6ZC8PKs4oBiefTwbKI61zw=",
64 | "dev": true,
65 | "requires": {
66 | "assertion-error": "^1.0.1",
67 | "check-error": "^1.0.1",
68 | "deep-eql": "^3.0.0",
69 | "get-func-name": "^2.0.0",
70 | "pathval": "^1.0.0",
71 | "type-detect": "^4.0.0"
72 | }
73 | },
74 | "chai-http": {
75 | "version": "4.0.0",
76 | "resolved": "https://registry.npmjs.org/chai-http/-/chai-http-4.0.0.tgz",
77 | "integrity": "sha512-R30Lj3JHHPhknOyurh09ZEBgyO4iSSeTjbLmyLvTr88IFC+zwRjAmaxBwj9TbEAGi0IV2uW+RHaTxeah5rdSaQ==",
78 | "dev": true,
79 | "requires": {
80 | "cookiejar": "^2.1.1",
81 | "is-ip": "^2.0.0",
82 | "methods": "^1.1.2",
83 | "qs": "^6.5.1",
84 | "superagent": "^3.7.0"
85 | }
86 | },
87 | "chai-xml": {
88 | "version": "0.3.2",
89 | "resolved": "https://registry.npmjs.org/chai-xml/-/chai-xml-0.3.2.tgz",
90 | "integrity": "sha512-HAyFPmJE0MEleo+sjWhJUxj+/aYBoUQg5EF/eGhbv1IZtx8mNGyGKD4jKlohhD4DsZaH5jay+Y/xtzBacBLdHw==",
91 | "dev": true,
92 | "requires": {
93 | "chai": "^1.9.1",
94 | "xml2js": "^0.4.4"
95 | },
96 | "dependencies": {
97 | "assertion-error": {
98 | "version": "1.0.0",
99 | "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.0.0.tgz",
100 | "integrity": "sha1-x/hUOP3UZrx8oWq5DIFRN5el0js=",
101 | "dev": true
102 | },
103 | "chai": {
104 | "version": "1.10.0",
105 | "resolved": "https://registry.npmjs.org/chai/-/chai-1.10.0.tgz",
106 | "integrity": "sha1-5AMcyHZURhp1lD5aNatG6vOcHrk=",
107 | "dev": true,
108 | "requires": {
109 | "assertion-error": "1.0.0",
110 | "deep-eql": "0.1.3"
111 | }
112 | },
113 | "deep-eql": {
114 | "version": "0.1.3",
115 | "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-0.1.3.tgz",
116 | "integrity": "sha1-71WKyrjeJSBs1xOQbXTlaTDrafI=",
117 | "dev": true,
118 | "requires": {
119 | "type-detect": "0.1.1"
120 | }
121 | },
122 | "type-detect": {
123 | "version": "0.1.1",
124 | "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-0.1.1.tgz",
125 | "integrity": "sha1-C6XsKohWQORw6k6FBZcZANrFiCI=",
126 | "dev": true
127 | }
128 | }
129 | },
130 | "check-error": {
131 | "version": "1.0.2",
132 | "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz",
133 | "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=",
134 | "dev": true
135 | },
136 | "cli-table": {
137 | "version": "0.3.1",
138 | "resolved": "https://registry.npmjs.org/cli-table/-/cli-table-0.3.1.tgz",
139 | "integrity": "sha1-9TsFJmqLGguTSz0IIebi3FkUriM=",
140 | "requires": {
141 | "colors": "1.0.3"
142 | }
143 | },
144 | "colors": {
145 | "version": "1.0.3",
146 | "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz",
147 | "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs="
148 | },
149 | "combined-stream": {
150 | "version": "1.0.6",
151 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz",
152 | "integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=",
153 | "dev": true,
154 | "requires": {
155 | "delayed-stream": "~1.0.0"
156 | }
157 | },
158 | "commander": {
159 | "version": "2.15.1",
160 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz",
161 | "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag=="
162 | },
163 | "component-emitter": {
164 | "version": "1.2.1",
165 | "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz",
166 | "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=",
167 | "dev": true
168 | },
169 | "content-disposition": {
170 | "version": "0.5.2",
171 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz",
172 | "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ="
173 | },
174 | "content-type": {
175 | "version": "1.0.4",
176 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
177 | "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="
178 | },
179 | "cookie": {
180 | "version": "0.3.1",
181 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz",
182 | "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s="
183 | },
184 | "cookie-signature": {
185 | "version": "1.0.6",
186 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
187 | "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
188 | },
189 | "cookiejar": {
190 | "version": "2.1.1",
191 | "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.1.tgz",
192 | "integrity": "sha1-Qa1XsbVVlR7BcUEqgZQrHoIA00o=",
193 | "dev": true
194 | },
195 | "core-util-is": {
196 | "version": "1.0.2",
197 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
198 | "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
199 | "dev": true
200 | },
201 | "debug": {
202 | "version": "2.6.9",
203 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
204 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
205 | "requires": {
206 | "ms": "2.0.0"
207 | }
208 | },
209 | "deep-eql": {
210 | "version": "3.0.1",
211 | "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz",
212 | "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==",
213 | "dev": true,
214 | "requires": {
215 | "type-detect": "^4.0.0"
216 | }
217 | },
218 | "delayed-stream": {
219 | "version": "1.0.0",
220 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
221 | "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=",
222 | "dev": true
223 | },
224 | "depd": {
225 | "version": "1.1.2",
226 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
227 | "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak="
228 | },
229 | "destroy": {
230 | "version": "1.0.4",
231 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
232 | "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
233 | },
234 | "dotenv": {
235 | "version": "6.0.0",
236 | "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-6.0.0.tgz",
237 | "integrity": "sha512-FlWbnhgjtwD+uNLUGHbMykMOYQaTivdHEmYwAKFjn6GKe/CqY0fNae93ZHTd20snh9ZLr8mTzIL9m0APQ1pjQg=="
238 | },
239 | "ee-first": {
240 | "version": "1.1.1",
241 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
242 | "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
243 | },
244 | "encodeurl": {
245 | "version": "1.0.2",
246 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
247 | "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
248 | },
249 | "escape-html": {
250 | "version": "1.0.3",
251 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
252 | "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
253 | },
254 | "etag": {
255 | "version": "1.8.1",
256 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
257 | "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
258 | },
259 | "express": {
260 | "version": "4.16.3",
261 | "resolved": "https://registry.npmjs.org/express/-/express-4.16.3.tgz",
262 | "integrity": "sha1-avilAjUNsyRuzEvs9rWjTSL37VM=",
263 | "requires": {
264 | "accepts": "~1.3.5",
265 | "array-flatten": "1.1.1",
266 | "body-parser": "1.18.2",
267 | "content-disposition": "0.5.2",
268 | "content-type": "~1.0.4",
269 | "cookie": "0.3.1",
270 | "cookie-signature": "1.0.6",
271 | "debug": "2.6.9",
272 | "depd": "~1.1.2",
273 | "encodeurl": "~1.0.2",
274 | "escape-html": "~1.0.3",
275 | "etag": "~1.8.1",
276 | "finalhandler": "1.1.1",
277 | "fresh": "0.5.2",
278 | "merge-descriptors": "1.0.1",
279 | "methods": "~1.1.2",
280 | "on-finished": "~2.3.0",
281 | "parseurl": "~1.3.2",
282 | "path-to-regexp": "0.1.7",
283 | "proxy-addr": "~2.0.3",
284 | "qs": "6.5.1",
285 | "range-parser": "~1.2.0",
286 | "safe-buffer": "5.1.1",
287 | "send": "0.16.2",
288 | "serve-static": "1.13.2",
289 | "setprototypeof": "1.1.0",
290 | "statuses": "~1.4.0",
291 | "type-is": "~1.6.16",
292 | "utils-merge": "1.0.1",
293 | "vary": "~1.1.2"
294 | }
295 | },
296 | "extend": {
297 | "version": "3.0.1",
298 | "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz",
299 | "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=",
300 | "dev": true
301 | },
302 | "finalhandler": {
303 | "version": "1.1.1",
304 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz",
305 | "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==",
306 | "requires": {
307 | "debug": "2.6.9",
308 | "encodeurl": "~1.0.2",
309 | "escape-html": "~1.0.3",
310 | "on-finished": "~2.3.0",
311 | "parseurl": "~1.3.2",
312 | "statuses": "~1.4.0",
313 | "unpipe": "~1.0.0"
314 | }
315 | },
316 | "flat": {
317 | "version": "4.0.0",
318 | "resolved": "https://registry.npmjs.org/flat/-/flat-4.0.0.tgz",
319 | "integrity": "sha512-ji/WMv2jdsE+LaznpkIF9Haax0sdpTBozrz/Dtg4qSRMfbs8oVg4ypJunIRYPiMLvH/ed6OflXbnbTIKJhtgeg==",
320 | "requires": {
321 | "is-buffer": "~1.1.5"
322 | }
323 | },
324 | "form-data": {
325 | "version": "2.3.2",
326 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.2.tgz",
327 | "integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=",
328 | "dev": true,
329 | "requires": {
330 | "asynckit": "^0.4.0",
331 | "combined-stream": "1.0.6",
332 | "mime-types": "^2.1.12"
333 | }
334 | },
335 | "formidable": {
336 | "version": "1.2.1",
337 | "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.1.tgz",
338 | "integrity": "sha512-Fs9VRguL0gqGHkXS5GQiMCr1VhZBxz0JnJs4JmMp/2jL18Fmbzvv7vOFRU+U8TBkHEE/CX1qDXzJplVULgsLeg==",
339 | "dev": true
340 | },
341 | "forwarded": {
342 | "version": "0.1.2",
343 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
344 | "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ="
345 | },
346 | "fresh": {
347 | "version": "0.5.2",
348 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
349 | "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac="
350 | },
351 | "get-func-name": {
352 | "version": "2.0.0",
353 | "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz",
354 | "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=",
355 | "dev": true
356 | },
357 | "hashlru": {
358 | "version": "2.2.1",
359 | "resolved": "https://registry.npmjs.org/hashlru/-/hashlru-2.2.1.tgz",
360 | "integrity": "sha1-EPIJmg18BaQPK+r1wdOc8vfavzY="
361 | },
362 | "http-errors": {
363 | "version": "1.6.3",
364 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz",
365 | "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=",
366 | "requires": {
367 | "depd": "~1.1.2",
368 | "inherits": "2.0.3",
369 | "setprototypeof": "1.1.0",
370 | "statuses": ">= 1.4.0 < 2"
371 | }
372 | },
373 | "iconv-lite": {
374 | "version": "0.4.19",
375 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz",
376 | "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ=="
377 | },
378 | "inherits": {
379 | "version": "2.0.3",
380 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
381 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
382 | },
383 | "ip-regex": {
384 | "version": "2.1.0",
385 | "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz",
386 | "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=",
387 | "dev": true
388 | },
389 | "ipaddr.js": {
390 | "version": "1.6.0",
391 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.6.0.tgz",
392 | "integrity": "sha1-4/o1e3c9phnybpXwSdBVxyeW+Gs="
393 | },
394 | "is-buffer": {
395 | "version": "1.1.6",
396 | "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
397 | "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="
398 | },
399 | "is-ip": {
400 | "version": "2.0.0",
401 | "resolved": "https://registry.npmjs.org/is-ip/-/is-ip-2.0.0.tgz",
402 | "integrity": "sha1-aO6gfooKCpTC0IDdZ0xzGrKkYas=",
403 | "dev": true,
404 | "requires": {
405 | "ip-regex": "^2.0.0"
406 | }
407 | },
408 | "isarray": {
409 | "version": "1.0.0",
410 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
411 | "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
412 | "dev": true
413 | },
414 | "js2xmlparser": {
415 | "version": "3.0.0",
416 | "resolved": "https://registry.npmjs.org/js2xmlparser/-/js2xmlparser-3.0.0.tgz",
417 | "integrity": "sha1-P7YOqgicVED5MZ9RdgzNB+JJlzM=",
418 | "requires": {
419 | "xmlcreate": "^1.0.1"
420 | }
421 | },
422 | "json2csv": {
423 | "version": "3.11.5",
424 | "resolved": "https://registry.npmjs.org/json2csv/-/json2csv-3.11.5.tgz",
425 | "integrity": "sha512-ORsw84BuRKMLxfI+HFZuvxRDnsJps53D5fIGr6tLn4ZY+ymcG8XU00E+JJ2wfAiHx5w2QRNmOLE8xHiGAeSfuQ==",
426 | "requires": {
427 | "cli-table": "^0.3.1",
428 | "commander": "^2.8.1",
429 | "debug": "^3.1.0",
430 | "flat": "^4.0.0",
431 | "lodash.clonedeep": "^4.5.0",
432 | "lodash.flatten": "^4.4.0",
433 | "lodash.get": "^4.4.0",
434 | "lodash.set": "^4.3.0",
435 | "lodash.uniq": "^4.5.0",
436 | "path-is-absolute": "^1.0.0"
437 | },
438 | "dependencies": {
439 | "debug": {
440 | "version": "3.1.0",
441 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
442 | "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
443 | "requires": {
444 | "ms": "2.0.0"
445 | }
446 | }
447 | }
448 | },
449 | "lodash.clonedeep": {
450 | "version": "4.5.0",
451 | "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
452 | "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8="
453 | },
454 | "lodash.flatten": {
455 | "version": "4.4.0",
456 | "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz",
457 | "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8="
458 | },
459 | "lodash.get": {
460 | "version": "4.4.2",
461 | "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz",
462 | "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk="
463 | },
464 | "lodash.set": {
465 | "version": "4.3.2",
466 | "resolved": "https://registry.npmjs.org/lodash.set/-/lodash.set-4.3.2.tgz",
467 | "integrity": "sha1-2HV7HagH3eJIFrDWqEvqGnYjCyM="
468 | },
469 | "lodash.uniq": {
470 | "version": "4.5.0",
471 | "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz",
472 | "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M="
473 | },
474 | "maxmind": {
475 | "version": "2.7.0",
476 | "resolved": "https://registry.npmjs.org/maxmind/-/maxmind-2.7.0.tgz",
477 | "integrity": "sha512-blmBnRbEkYBGz+7kxNQzuYUwyMyJ5PYGhrMYe6RXmMSBxZXe2665INgNP5/8WuYdWnDJ1Z8osC/LVKdsbT2fnQ==",
478 | "requires": {
479 | "big-integer": "^1.6.31",
480 | "tiny-lru": "^1.6.1"
481 | }
482 | },
483 | "media-typer": {
484 | "version": "0.3.0",
485 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
486 | "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
487 | },
488 | "merge-descriptors": {
489 | "version": "1.0.1",
490 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
491 | "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E="
492 | },
493 | "methods": {
494 | "version": "1.1.2",
495 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
496 | "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4="
497 | },
498 | "mime": {
499 | "version": "1.4.1",
500 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz",
501 | "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ=="
502 | },
503 | "mime-db": {
504 | "version": "1.33.0",
505 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz",
506 | "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ=="
507 | },
508 | "mime-types": {
509 | "version": "2.1.18",
510 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz",
511 | "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==",
512 | "requires": {
513 | "mime-db": "~1.33.0"
514 | }
515 | },
516 | "mmdb-reader": {
517 | "version": "1.2.0",
518 | "resolved": "https://registry.npmjs.org/mmdb-reader/-/mmdb-reader-1.2.0.tgz",
519 | "integrity": "sha512-H6B2VFogE+2VM/1qxEhGWbIxOy6hATZ1vx1uW7/8wIltC0+4vOirnyeqquCslCXCEIgJ4VGrSxknL3BhNzkASg==",
520 | "requires": {
521 | "big-integer": "^1.5.4",
522 | "hashlru": "^2.0.0"
523 | }
524 | },
525 | "ms": {
526 | "version": "2.0.0",
527 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
528 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
529 | },
530 | "negotiator": {
531 | "version": "0.6.1",
532 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz",
533 | "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk="
534 | },
535 | "on-finished": {
536 | "version": "2.3.0",
537 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
538 | "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
539 | "requires": {
540 | "ee-first": "1.1.1"
541 | }
542 | },
543 | "parseurl": {
544 | "version": "1.3.2",
545 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz",
546 | "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M="
547 | },
548 | "path-is-absolute": {
549 | "version": "1.0.1",
550 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
551 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
552 | },
553 | "path-to-regexp": {
554 | "version": "0.1.7",
555 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
556 | "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
557 | },
558 | "pathval": {
559 | "version": "1.1.0",
560 | "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz",
561 | "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=",
562 | "dev": true
563 | },
564 | "process-nextick-args": {
565 | "version": "2.0.0",
566 | "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz",
567 | "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==",
568 | "dev": true
569 | },
570 | "proxy-addr": {
571 | "version": "2.0.3",
572 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.3.tgz",
573 | "integrity": "sha512-jQTChiCJteusULxjBp8+jftSQE5Obdl3k4cnmLA6WXtK6XFuWRnvVL7aCiBqaLPM8c4ph0S4tKna8XvmIwEnXQ==",
574 | "requires": {
575 | "forwarded": "~0.1.2",
576 | "ipaddr.js": "1.6.0"
577 | }
578 | },
579 | "qs": {
580 | "version": "6.5.1",
581 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz",
582 | "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A=="
583 | },
584 | "range-parser": {
585 | "version": "1.2.0",
586 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz",
587 | "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4="
588 | },
589 | "raw-body": {
590 | "version": "2.3.2",
591 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.2.tgz",
592 | "integrity": "sha1-vNYMd9Prk83gBQKVw/N5OJvIj4k=",
593 | "requires": {
594 | "bytes": "3.0.0",
595 | "http-errors": "1.6.2",
596 | "iconv-lite": "0.4.19",
597 | "unpipe": "1.0.0"
598 | },
599 | "dependencies": {
600 | "depd": {
601 | "version": "1.1.1",
602 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz",
603 | "integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k="
604 | },
605 | "http-errors": {
606 | "version": "1.6.2",
607 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz",
608 | "integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=",
609 | "requires": {
610 | "depd": "1.1.1",
611 | "inherits": "2.0.3",
612 | "setprototypeof": "1.0.3",
613 | "statuses": ">= 1.3.1 < 2"
614 | }
615 | },
616 | "setprototypeof": {
617 | "version": "1.0.3",
618 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz",
619 | "integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ="
620 | }
621 | }
622 | },
623 | "readable-stream": {
624 | "version": "2.3.6",
625 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
626 | "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
627 | "dev": true,
628 | "requires": {
629 | "core-util-is": "~1.0.0",
630 | "inherits": "~2.0.3",
631 | "isarray": "~1.0.0",
632 | "process-nextick-args": "~2.0.0",
633 | "safe-buffer": "~5.1.1",
634 | "string_decoder": "~1.1.1",
635 | "util-deprecate": "~1.0.1"
636 | }
637 | },
638 | "safe-buffer": {
639 | "version": "5.1.1",
640 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz",
641 | "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg=="
642 | },
643 | "sax": {
644 | "version": "1.2.4",
645 | "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
646 | "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==",
647 | "dev": true
648 | },
649 | "send": {
650 | "version": "0.16.2",
651 | "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz",
652 | "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==",
653 | "requires": {
654 | "debug": "2.6.9",
655 | "depd": "~1.1.2",
656 | "destroy": "~1.0.4",
657 | "encodeurl": "~1.0.2",
658 | "escape-html": "~1.0.3",
659 | "etag": "~1.8.1",
660 | "fresh": "0.5.2",
661 | "http-errors": "~1.6.2",
662 | "mime": "1.4.1",
663 | "ms": "2.0.0",
664 | "on-finished": "~2.3.0",
665 | "range-parser": "~1.2.0",
666 | "statuses": "~1.4.0"
667 | }
668 | },
669 | "serve-static": {
670 | "version": "1.13.2",
671 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz",
672 | "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==",
673 | "requires": {
674 | "encodeurl": "~1.0.2",
675 | "escape-html": "~1.0.3",
676 | "parseurl": "~1.3.2",
677 | "send": "0.16.2"
678 | }
679 | },
680 | "setprototypeof": {
681 | "version": "1.1.0",
682 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz",
683 | "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ=="
684 | },
685 | "statuses": {
686 | "version": "1.4.0",
687 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz",
688 | "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew=="
689 | },
690 | "string_decoder": {
691 | "version": "1.1.1",
692 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
693 | "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
694 | "dev": true,
695 | "requires": {
696 | "safe-buffer": "~5.1.0"
697 | }
698 | },
699 | "superagent": {
700 | "version": "3.8.2",
701 | "resolved": "https://registry.npmjs.org/superagent/-/superagent-3.8.2.tgz",
702 | "integrity": "sha512-gVH4QfYHcY3P0f/BZzavLreHW3T1v7hG9B+hpMQotGQqurOvhv87GcMCd6LWySmBuf+BDR44TQd0aISjVHLeNQ==",
703 | "dev": true,
704 | "requires": {
705 | "component-emitter": "^1.2.0",
706 | "cookiejar": "^2.1.0",
707 | "debug": "^3.1.0",
708 | "extend": "^3.0.0",
709 | "form-data": "^2.3.1",
710 | "formidable": "^1.1.1",
711 | "methods": "^1.1.1",
712 | "mime": "^1.4.1",
713 | "qs": "^6.5.1",
714 | "readable-stream": "^2.0.5"
715 | },
716 | "dependencies": {
717 | "debug": {
718 | "version": "3.1.0",
719 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
720 | "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
721 | "dev": true,
722 | "requires": {
723 | "ms": "2.0.0"
724 | }
725 | }
726 | }
727 | },
728 | "tiny-lru": {
729 | "version": "1.6.1",
730 | "resolved": "https://registry.npmjs.org/tiny-lru/-/tiny-lru-1.6.1.tgz",
731 | "integrity": "sha512-m8oyPnHjnQlbDk8+MCw33qRMp6+BxPxoayN9C743VToeyQ5zZV6F6vkklrYVEI0z9MQ3+jmc+22tKmvPg4gmoA=="
732 | },
733 | "type-detect": {
734 | "version": "4.0.8",
735 | "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz",
736 | "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==",
737 | "dev": true
738 | },
739 | "type-is": {
740 | "version": "1.6.16",
741 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz",
742 | "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==",
743 | "requires": {
744 | "media-typer": "0.3.0",
745 | "mime-types": "~2.1.18"
746 | }
747 | },
748 | "unpipe": {
749 | "version": "1.0.0",
750 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
751 | "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
752 | },
753 | "util-deprecate": {
754 | "version": "1.0.2",
755 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
756 | "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
757 | "dev": true
758 | },
759 | "utils-merge": {
760 | "version": "1.0.1",
761 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
762 | "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
763 | },
764 | "vary": {
765 | "version": "1.1.2",
766 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
767 | "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
768 | },
769 | "xml2js": {
770 | "version": "0.4.19",
771 | "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz",
772 | "integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==",
773 | "dev": true,
774 | "requires": {
775 | "sax": ">=0.6.0",
776 | "xmlbuilder": "~9.0.1"
777 | }
778 | },
779 | "xmlbuilder": {
780 | "version": "9.0.7",
781 | "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz",
782 | "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=",
783 | "dev": true
784 | },
785 | "xmlcreate": {
786 | "version": "1.0.2",
787 | "resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-1.0.2.tgz",
788 | "integrity": "sha1-+mv3YqYKQT+z3Y9LA8WyaSONMI8="
789 | }
790 | }
791 | }
792 |
--------------------------------------------------------------------------------
/server/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "geoip",
3 | "version": "0.2.5",
4 | "description": "API, geolocation IP",
5 | "main": "geoip.js",
6 | "keywords": [],
7 | "scripts": {
8 | "test": "mocha test/*.js --timeout 15000",
9 | "start": "node geoip.js"
10 | },
11 | "repository": {
12 | "type": "git",
13 | "url": "git+https://github.com/jolav/geoip-tools.git"
14 | },
15 | "author": "jolav",
16 | "license": "BSD-3-Clause",
17 | "bugs": {
18 | "url": "https://github.com/jolav/geoip-tools/issues"
19 | },
20 | "homepage": "https://github.com/jolav/geoip-tools#readme",
21 | "dependencies": {
22 | "dotenv": "^6.0.0",
23 | "express": "^4.16.3",
24 | "js2xmlparser": "^3.0.0",
25 | "json2csv": "^3.11.5",
26 | "maxmind": "^2.7.0",
27 | "mmdb-reader": "^1.2.0"
28 | },
29 | "devDependencies": {
30 | "chai": "^4.1.2",
31 | "chai-http": "^4.0.0",
32 | "chai-xml": "^0.3.2"
33 | }
34 | }
--------------------------------------------------------------------------------
/server/test/geoip_test.js:
--------------------------------------------------------------------------------
1 | /* */
2 |
3 | const chai = require('chai');
4 | const chaiHttp = require('chai-http');
5 | const chaiXml = require('chai-xml');
6 |
7 | const expect = require('chai').expect;
8 |
9 | chai.use(chaiHttp);
10 | chai.use(chaiXml);
11 |
12 | // const url = 'https://geoip.tools/v1'
13 | const url = 'http://localhost:3000';
14 |
15 | describe('GEOIP TEST JSON', function () {
16 | before(function (done) {
17 | done();
18 | });
19 | it('JSON /json no parameters', function (done) {
20 | chai.request(url)
21 | .get('/json')
22 | .query({})
23 | .end(function (err, res) {
24 | // console.log('PATH=> ', res.req.path)
25 | expect(err).to.be.null;
26 | expect(res).to.have.status(200);
27 | expect(res).to.be.json;
28 | expect(res.body).to.be.an('object');
29 | expect(res.body).to.have.property('ip');
30 | expect(res.body).to.have.property('country_code');
31 | expect(res.body).to.have.property('country_name');
32 | expect(res.body).to.have.property('region_code');
33 | expect(res.body).to.have.property('region_name');
34 | expect(res.body).to.have.property('city');
35 | expect(res.body).to.have.property('zip_code');
36 | expect(res.body).to.have.property('time_zone');
37 | expect(res.body).to.have.property('latitude');
38 | expect(res.body).to.have.property('longitude');
39 | done();
40 | });
41 | });
42 | it('JSON /json?q=208.67.222.222 valid ip', function (done) {
43 | chai.request(url)
44 | .get('/json')
45 | .query({q: '208.67.222.222'})
46 | .end(function (err, res) {
47 | // console.log('PATH=> ', res.req.path)
48 | expect(err).to.be.null;
49 | expect(res).to.have.status(200);
50 | expect(res).to.be.json;
51 | expect(res.body).to.be.an('object');
52 | expect(res.body).to.have.property('ip', '208.67.222.222');
53 | expect(res.body).to.have.property('country_code', 'US');
54 | expect(res.body).to.have.property('country_name', 'United States');
55 | expect(res.body).to.have.property('region_code', 'CA');
56 | expect(res.body).to.have.property('region_name', 'California');
57 | expect(res.body).to.have.property('city', 'San Francisco');
58 | expect(res.body).to.have.property('zip_code', '94107');
59 | expect(res.body).to.have.property('time_zone', 'America/Los_Angeles');
60 | expect(res.body).to.have.property('latitude', '37.7697');
61 | expect(res.body).to.have.property('longitude', '-122.3933');
62 | done();
63 | });
64 | });
65 | it('JSON /json?q=8.8.8.8 valid ip', function (done) {
66 | chai.request(url)
67 | .get('/json')
68 | .query({q: '8.8.8.8'})
69 | .end(function (err, res) {
70 | // console.log('PATH=> ', res.req.path)
71 | expect(err).to.be.null;
72 | expect(res).to.have.status(200);
73 | expect(res).to.be.json;
74 | expect(res.body).to.be.an('object');
75 | expect(res.body).to.have.property('ip', '8.8.8.8');
76 | expect(res.body).to.have.property('country_code', 'US');
77 | expect(res.body).to.have.property('country_name', 'United States');
78 | expect(res.body).to.have.property('region_code', '');
79 | expect(res.body).to.have.property('region_name', '');
80 | expect(res.body).to.have.property('city', '');
81 | expect(res.body).to.have.property('zip_code', '');
82 | expect(res.body).to.have.property('time_zone', '');
83 | expect(res.body).to.have.property('latitude', '37.7510');
84 | expect(res.body).to.have.property('longitude', '-97.8220');
85 | done();
86 | });
87 | });
88 | it('JSON /json?q=2a00:1450:4006:803::200e valid ipv6', function (done) {
89 | chai.request(url)
90 | .get('/json')
91 | .query({q: '2a00:1450:4006:803::200e'})
92 | .end(function (err, res) {
93 | // console.log('PATH=> ', res.req.path)
94 | expect(err).to.be.null;
95 | expect(res).to.have.status(200);
96 | expect(res).to.be.json;
97 | expect(res.body).to.be.an('object');
98 | expect(res.body).to.have.property('ip', '2a00:1450:4006:803::200e');
99 | expect(res.body).to.have.property('country_code', 'IE');
100 | expect(res.body).to.have.property('country_name', 'Ireland');
101 | expect(res.body).to.have.property('region_code', '');
102 | expect(res.body).to.have.property('region_name', '');
103 | expect(res.body).to.have.property('city', '');
104 | expect(res.body).to.have.property('zip_code', '');
105 | expect(res.body).to.have.property('time_zone', 'Europe/Dublin');
106 | expect(res.body).to.have.property('latitude', '53.0000');
107 | expect(res.body).to.have.property('longitude', '-8.0000');
108 | done();
109 | });
110 | });
111 | it('JSON /json?q=260.50.50.50 no valid ip', function (done) {
112 | chai.request(url)
113 | .get('/json')
114 | .query({q: '260.50.50.50'})
115 | .end(function (err, res) {
116 | // console.log('PATH=> ', res.req.path)
117 | expect(err).to.be.null;
118 | expect(res).to.have.status(400);
119 | expect(res).to.be.json;
120 | expect(res.body).to.equal(text.json[0]);
121 | done();
122 | });
123 | });
124 | it('JSON /json?q=20.20.-5.20 no valid ip', function (done) {
125 | chai.request(url)
126 | .get('/json')
127 | .query({q: '20.20.-5.20'})
128 | .end(function (err, res) {
129 | // console.log('PATH=> ', res.req.path)
130 | expect(err).to.be.null;
131 | expect(res).to.have.status(400);
132 | expect(res).to.be.json;
133 | expect(res.body).to.equal(text.json[1]);
134 | done();
135 | });
136 | });
137 | it('JSON /json?q=github.com valid hostname', function (done) {
138 | chai.request(url)
139 | .get('/json')
140 | .query({q: 'github.com'})
141 | .end(function (err, res) {
142 | // console.log('PATH=> ', res.req.path)
143 | expect(err).to.be.null;
144 | expect(res).to.have.status(200);
145 | expect(res).to.be.json;
146 | expect(res.body).to.be.an('object');
147 | // expect(res.body).to.have.property('ip', '192.30.253.112')
148 | expect(res.body.ip).to.be.oneOf(['192.30.253.112', '192.30.253.113']);
149 | expect(res.body).to.have.property('country_code', 'US');
150 | expect(res.body).to.have.property('country_name', 'United States');
151 | expect(res.body).to.have.property('region_code', 'CA');
152 | expect(res.body).to.have.property('region_name', 'California');
153 | expect(res.body).to.have.property('city', 'San Francisco');
154 | expect(res.body).to.have.property('zip_code', '94107');
155 | expect(res.body).to.have.property('time_zone', 'America/Los_Angeles');
156 | expect(res.body).to.have.property('latitude', '37.7697');
157 | expect(res.body).to.have.property('longitude', '-122.3933');
158 | done();
159 | });
160 | });
161 | it('JSON /json?q=no-valid.com no valid hostname', function (done) {
162 | chai.request(url)
163 | .get('/json')
164 | .query({q: 'no-valid.com'})
165 | .end(function (err, res) {
166 | // console.log('PATH=> ', res.req.path)
167 | expect(err).to.be.null;
168 | expect(res).to.have.status(400);
169 | expect(res).to.be.json;
170 | expect(res.body).to.equal(text.json[2]);
171 | done();
172 | });
173 | });
174 | });
175 |
176 | describe('GEOIP TEST XML', function () {
177 | before(function (done) {
178 | done();
179 | });
180 | it('XML /xml no parameters', function (done) {
181 | chai.request(url)
182 | .get('/xml')
183 | // .query({q: '8.8.8.8'})
184 | .end(function (err, res) {
185 | expect(err).to.be.null;
186 | expect(res).to.have.status(200);
187 | expect(res.text).xml.to.be.valid();
188 | done();
189 | });
190 | });
191 | it('XML /xml?q=208.67.222.222 valid ip ', function (done) {
192 | chai.request(url)
193 | .get('/xml')
194 | .query({q: '208.67.222.222'})
195 | .end(function (err, res) {
196 | expect(err).to.be.null;
197 | expect(res).to.have.status(200);
198 | expect(res.text).xml.to.be.valid();
199 | expect(res.text).xml.to.equal(text.xml[1]);
200 | done();
201 | });
202 | });
203 | it('XML /xml?q=8.8.8.8 valid ip ', function (done) {
204 | chai.request(url)
205 | .get('/xml')
206 | .query({q: '8.8.8.8'})
207 | .end(function (err, res) {
208 | expect(err).to.be.null;
209 | expect(res).to.have.status(200);
210 | expect(res.text).xml.to.be.valid();
211 | expect(res.text).xml.to.equal(text.xml[2]);
212 | done();
213 | });
214 | });
215 | it('XML /xml?q=2a00:1450:4006:803::200e valid ipv6 ', function (done) {
216 | chai.request(url)
217 | .get('/xml')
218 | .query({q: '2a00:1450:4006:803::200e'})
219 | .end(function (err, res) {
220 | expect(err).to.be.null;
221 | expect(res).to.have.status(200);
222 | expect(res.text).xml.to.be.valid();
223 | expect(res.text).xml.to.equal(text.xml[3]);
224 | done();
225 | });
226 | });
227 | it('XML /xml?q=260.50.50.50 no valid ip ', function (done) {
228 | chai.request(url)
229 | .get('/xml')
230 | .query({q: '260.50.50.50'})
231 | .end(function (err, res) {
232 | expect(err).to.be.null;
233 | expect(res).to.have.status(400);
234 | expect(res.body).to.equal(text.xml[4]);
235 | done();
236 | });
237 | });
238 | it('XML /xml?q=20.20.-5.20 no valid ip ', function (done) {
239 | chai.request(url)
240 | .get('/xml')
241 | .query({q: '20.20.-5.20'})
242 | .end(function (err, res) {
243 | expect(err).to.be.null;
244 | expect(res).to.have.status(400);
245 | expect(res.body).to.equal(text.xml[5]);
246 | done();
247 | });
248 | });
249 | it('XML /xml?q=github.com valid hostname ', function (done) {
250 | chai.request(url)
251 | .get('/xml')
252 | .query({q: 'github.com'})
253 | .end(function (err, res) {
254 | expect(err).to.be.null;
255 | expect(res).to.have.status(200);
256 | expect(res.text).xml.to.be.valid();
257 | if (res.text.indexOf('192.30.253.113') === -1) {
258 | expect(res.text).xml.to.equal(text.xml[6]);
259 | } else {
260 | expect(res.text).xml.to.equal(text.xml[7]);
261 | }
262 | done();
263 | });
264 | });
265 | it('XML /xml?q=no-valid.com ', function (done) {
266 | chai.request(url)
267 | .get('/xml')
268 | .query({q: 'no-valid.com'})
269 | .end(function (err, res) {
270 | expect(err).to.be.null;
271 | expect(res).to.have.status(400);
272 | expect(res.body).to.equal(text.xml[8]);
273 | done();
274 | });
275 | });
276 | });
277 |
278 | const text = {
279 | 'json': [
280 | '260.50.50.50 is a unknown host, not a valid IP or hostname',
281 | '20.20.-5.20 is a unknown host, not a valid IP or hostname',
282 | 'no-valid.com is a unknown host, not a valid IP or hostname'
283 | ],
284 | 'xml': [
285 | '',
286 |
287 | '208.67.222.222 US United States CA California San Francisco 94107 America/Los_Angeles 37.7697 -122.3933 ',
288 |
289 | '8.8.8.8 US United States 37.7510 -97.8220 ',
290 |
291 | '2a00:1450:4006:803::200e IE Ireland Europe/Dublin 53.0000 -8.0000 ',
292 |
293 | '260.50.50.50 is a unknown host, not a valid IP or hostname',
294 |
295 | '20.20.-5.20 is a unknown host, not a valid IP or hostname',
296 |
297 | '192.30.253.112 US United States CA California San Francisco 94107 America/Los_Angeles 37.7697 -122.3933 ',
298 |
299 | '192.30.253.113 US United States CA California San Francisco 94107 America/Los_Angeles 37.7697 -122.3933 ',
300 |
301 | 'no-valid.com is a unknown host, not a valid IP or hostname'
302 |
303 | ]
304 | };
305 |
--------------------------------------------------------------------------------