-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 01f7c94
Showing
13 changed files
with
4,674 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
{ | ||
"imageSize": 75, | ||
"projectName": "ip-geolocation", | ||
"projectOwner": "devoxa", | ||
"repoType": "github", | ||
"repoHost": "https://github.com", | ||
"skipCi": true, | ||
"contributors": [ | ||
{ | ||
"login": "queicherius", | ||
"name": "David Reeß", | ||
"avatar_url": "https://avatars3.githubusercontent.com/u/4615516?v=4", | ||
"profile": "https://www.david-reess.de", | ||
"contributions": [ | ||
"code", | ||
"doc", | ||
"test" | ||
] | ||
} | ||
], | ||
"files": [ | ||
"README.md" | ||
], | ||
"contributorsPerLine": 7 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
version: 2.1 | ||
|
||
orbs: | ||
node: circleci/[email protected] | ||
|
||
jobs: | ||
build-and-test: | ||
executor: | ||
name: node/default | ||
steps: | ||
- checkout | ||
- node/with-cache: | ||
steps: | ||
- run: yarn install | ||
- run: node scripts/postinstall.js src | ||
- run: yarn test --coverage | ||
- run: bash <(curl -s https://codecov.io/bash) | ||
- run: yarn format:check | ||
- run: yarn lint | ||
- run: yarn build | ||
|
||
workflows: | ||
build-and-test: | ||
jobs: | ||
- build-and-test |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
# Downloaded Binaries | ||
src/dbip-city-lite.mmdb | ||
src/version.json | ||
|
||
# Compiled Output | ||
dist/ | ||
|
||
# Tests | ||
coverage/ | ||
|
||
# Dependencies | ||
node_modules/ | ||
yarn-debug.log* | ||
yarn-error.log* | ||
|
||
# Development Environments | ||
.history/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
# Downloaded Binaries | ||
src/dbip-city-lite.mmdb | ||
src/version.json | ||
|
||
# Tests | ||
coverage/ | ||
|
||
# Dependencies | ||
node_modules/ | ||
yarn-debug.log* | ||
yarn-error.log* | ||
|
||
# Development Environments | ||
.history/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
<!-- Title --> | ||
<h1 align="center"> | ||
ip-geolocation | ||
</h1> | ||
|
||
<!-- Description --> | ||
<h4 align="center"> | ||
Resolve an IP address into a geolocation (continent, country, subdivision, city & lat/long) | ||
</h4> | ||
|
||
<!-- Badges --> | ||
<p align="center"> | ||
<a href="https://www.npmjs.com/package/@devoxa/ip-geolocation"> | ||
<img | ||
src="https://img.shields.io/npm/v/@devoxa/ip-geolocation?style=flat-square" | ||
alt="Package Version" | ||
/> | ||
</a> | ||
|
||
<a href="https://app.circleci.com/pipelines/github/devoxa/ip-geolocation?branch=master"> | ||
<img | ||
src="https://img.shields.io/circleci/build/github/devoxa/ip-geolocation/master?style=flat-square" | ||
alt="Build Status" | ||
/> | ||
</a> | ||
|
||
<a href="https://codecov.io/github/devoxa/ip-geolocation"> | ||
<img | ||
src="https://img.shields.io/codecov/c/github/devoxa/ip-geolocation/master?style=flat-square" | ||
alt="Code Coverage" | ||
/> | ||
</a> | ||
</p> | ||
|
||
<!-- Quicklinks --> | ||
<p align="center"> | ||
<a href="#installation">Installation</a> • | ||
<a href="#usage">Usage</a> • | ||
<a href="#contributors">Contributors</a> • | ||
<a href="#license">License :warning:</a> | ||
</p> | ||
|
||
<br> | ||
|
||
## Installation | ||
|
||
:warning: **Before installing this make sure you understand the [License](#license)!** | ||
|
||
```bash | ||
yarn add @devoxa/ip-geolocation | ||
``` | ||
|
||
**This module will automatically download a ~85MB IP geolocation database from | ||
[db-ip.com](https://db-ip.com/db/download/ip-to-city-lite) in a postinstall step.** | ||
|
||
## Usage | ||
|
||
```ts | ||
import { loadDatabase, geolocateIp } from '@devoxa/ip-geolocation' | ||
|
||
// Preload the geolocation database | ||
// Recommended, but not required. Will reduce the first call to `geolocateIp` by ~100ms | ||
await loadDatabase() | ||
|
||
// Lookup an IP address | ||
const result = await geolocateIp('69.10.63.243') | ||
// { | ||
// continent: { code: 'NA', name: 'North America' }, | ||
// country: { code: 'US', name: 'United States', isInEuropeanUnion: false }, | ||
// subdivision: { name: 'New Jersey' }, | ||
// city: { name: 'Secaucus' }, | ||
// location: { latitude: 40.7861, longitude: -74.0743 }, | ||
// } | ||
``` | ||
|
||
## Contributors | ||
|
||
Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)): | ||
|
||
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section --> | ||
<!-- prettier-ignore-start --> | ||
<!-- markdownlint-disable --> | ||
<table> | ||
<tr> | ||
<td align="center"><a href="https://www.david-reess.de"><img src="https://avatars3.githubusercontent.com/u/4615516?v=4" width="75px;" alt=""/><br /><sub><b>David Reeß</b></sub></a><br /><a href="https://github.com/devoxa/ip-geolocation/commits?author=queicherius" title="Code">💻</a> <a href="https://github.com/devoxa/ip-geolocation/commits?author=queicherius" title="Documentation">📖</a> <a href="https://github.com/devoxa/ip-geolocation/commits?author=queicherius" title="Tests">⚠️</a></td> | ||
</tr> | ||
</table> | ||
|
||
<!-- markdownlint-enable --> | ||
<!-- prettier-ignore-end --> | ||
|
||
<!-- ALL-CONTRIBUTORS-LIST:END --> | ||
|
||
This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) | ||
specification. Contributions of any kind welcome! | ||
|
||
## License | ||
|
||
MIT | ||
|
||
:warning: The module will also automatically download and use a ~85MB IP geolocation database from | ||
[db-ip.com](https://db-ip.com/db/download/ip-to-city-lite). This database is licensed under a | ||
**Creative Commons Attribution 4.0 International License**. You are free to use this database | ||
in your application, provided you give attribution to DB-IP.com for the data. | ||
|
||
In the case of a web application, you must include a link back to DB-IP.com on pages that display or | ||
use results from the database: | ||
|
||
```html | ||
<a href="https://db-ip.com">IP Geolocation by DB-IP</a> | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
module.exports = { | ||
preset: 'ts-jest', | ||
testEnvironment: 'node', | ||
testPathIgnorePatterns: ['dist'], | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
{ | ||
"name": "@devoxa/ip-geolocation", | ||
"description": "Resolve an IP address into a geolocation (continent, country, subdivision, city & lat/long)", | ||
"version": "0.1.0", | ||
"main": "dist/src/index.js", | ||
"license": "MIT", | ||
"repository": { | ||
"url": "https://github.com/devoxa/ip-geolocation" | ||
}, | ||
"scripts": { | ||
"test": "jest", | ||
"format": "prettier --ignore-path='.gitignore' --list-different --write .", | ||
"format:check": "prettier --ignore-path='.gitignore' --check .", | ||
"lint": "eslint --ignore-path='.gitignore' '{src,tests}/**/*.ts'", | ||
"build": "rm -rf dist/ && tsc", | ||
"preversion": "yarn build" | ||
}, | ||
"eslintConfig": { | ||
"extends": "@devoxa" | ||
}, | ||
"prettier": "@devoxa/prettier-config", | ||
"dependencies": { | ||
"maxmind": "^4.1.3" | ||
}, | ||
"devDependencies": { | ||
"@devoxa/eslint-config": "^1.0.0", | ||
"@devoxa/prettier-config": "^1.0.0", | ||
"@types/jest": "^25.2.2", | ||
"@types/node": "^14.0.1", | ||
"eslint": "^7.0.0", | ||
"jest": "^26.0.1", | ||
"prettier": "^2.0.5", | ||
"ts-jest": "^26.0.0", | ||
"typescript": "^3.9.2" | ||
}, | ||
"publishConfig": { | ||
"access": "public" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
const fs = require('fs') | ||
const https = require('https') | ||
const path = require('path') | ||
const zlib = require('zlib') | ||
|
||
const folder = process.argv[2] || 'dist/src' | ||
|
||
const DOWNLOAD_LINK = 'https://download.db-ip.com/free/dbip-city-lite-YYYY-MM.mmdb.gz' | ||
const DOWNLOAD_PATH = path.join(__dirname, '..', folder) | ||
const DOWNLOAD_PATH_VERSION = path.join(DOWNLOAD_PATH, 'version.json') | ||
const DOWNLOAD_PATH_MMDB = path.join(DOWNLOAD_PATH, 'dbip-city-lite.mmdb') | ||
|
||
run() | ||
|
||
async function run() { | ||
const date = new Date() | ||
|
||
// Try to download the latest version of the last 3 months | ||
for (let i = 0; i !== 3; i++) { | ||
date.setMonth(date.getMonth() - i) | ||
|
||
try { | ||
await downloadForDate(date) | ||
} catch (err) { | ||
console.warn(err) | ||
} | ||
} | ||
|
||
console.error('Failed downloading DBIP City Lite database') | ||
process.exit(1) | ||
} | ||
|
||
async function downloadForDate(date) { | ||
const version = buildVersionStringForDate(date) | ||
|
||
if (versionExists(version)) { | ||
console.log(`Skipped download because database for ${version} already exists`) | ||
process.exit(0) | ||
} | ||
|
||
console.log(`Downloading DBIP City Lite database for ${version}...`) | ||
const downloadUrl = buildDownloadUrl(version) | ||
await downloadToFile(downloadUrl, DOWNLOAD_PATH_MMDB) | ||
|
||
console.log(`Writing version file for ${version}...`) | ||
fs.writeFileSync(DOWNLOAD_PATH_VERSION, JSON.stringify({ version }), 'utf-8') | ||
|
||
process.exit(0) | ||
} | ||
|
||
function buildVersionStringForDate(date) { | ||
return `${date.getFullYear()}-${(date.getMonth() + 1 + '').padStart(2, '0')}` | ||
} | ||
|
||
function versionExists(version) { | ||
if (!fs.existsSync(DOWNLOAD_PATH_MMDB)) { | ||
return false | ||
} | ||
|
||
try { | ||
const versionJson = JSON.parse(fs.readFileSync(DOWNLOAD_PATH_VERSION, 'utf-8')) | ||
return versionJson.version === version | ||
} catch (err) { | ||
return false | ||
} | ||
} | ||
|
||
function buildDownloadUrl(version) { | ||
return DOWNLOAD_LINK.replace('YYYY-MM', version) | ||
} | ||
|
||
function downloadToFile(url, path) { | ||
return new Promise((resolve, reject) => { | ||
https.get(url, (response) => { | ||
if (response.statusCode >= 400) { | ||
return reject(`${url} responded with ${response.statusCode}`) | ||
} | ||
|
||
// Pipe the response into `gunzip` to inflate the gzip compression, and then | ||
// into the file write stream, and resolve once we are done. | ||
const writeStream = fs.createWriteStream(path) | ||
writeStream.on('finish', () => resolve()) | ||
|
||
response.pipe(zlib.createGunzip()).pipe(writeStream) | ||
}) | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
import mmdbReader, { Reader } from 'maxmind' | ||
import fs from 'fs' | ||
import path from 'path' | ||
|
||
export interface GeolocateIpResult { | ||
continent: { code: string; name: string } | ||
country: { code: string; name: string; isInEuropeanUnion: boolean } | ||
subdivision: { name: string } | ||
city: { name: string } | ||
location: { latitude: number; longitude: number } | ||
} | ||
|
||
let reader: Reader<any> | ||
const DB_FILE_PATH = path.join(__dirname, 'dbip-city-lite.mmdb') | ||
|
||
export async function loadDatabase(dbFilePath: string = DB_FILE_PATH) { | ||
if (reader) { | ||
return | ||
} | ||
|
||
const dbFileExists = await fileExists(dbFilePath) | ||
if (!dbFileExists) { | ||
throw new Error(`Database file at ${dbFilePath} does not exist`) | ||
} | ||
|
||
reader = await mmdbReader.open(dbFilePath) | ||
} | ||
|
||
function fileExists(path: string) { | ||
return new Promise((resolve) => fs.access(path, fs.constants.F_OK, (err) => resolve(!err))) | ||
} | ||
|
||
export async function geolocateIp(ip: string): Promise<GeolocateIpResult | null> { | ||
await loadDatabase() | ||
|
||
const result = reader.get(ip) | ||
|
||
if (!result) { | ||
return null | ||
} | ||
|
||
return { | ||
continent: { | ||
code: result.continent.code, | ||
name: result.continent.names.en, | ||
}, | ||
country: { | ||
code: result.country.iso_code, | ||
name: result.country.names.en, | ||
isInEuropeanUnion: result.country.is_in_european_union, | ||
}, | ||
subdivision: { | ||
name: result.subdivisions[0].names.en, | ||
}, | ||
city: { | ||
name: result.city.names.en, | ||
}, | ||
location: { | ||
latitude: result.location.latitude, | ||
longitude: result.location.longitude, | ||
}, | ||
} | ||
} |
Oops, something went wrong.