diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..c635ccc --- /dev/null +++ b/.travis.yml @@ -0,0 +1,8 @@ +sudo: false +language: node_js +node_js: + - stable + +script: + - npm run lint + - npm test diff --git a/README.md b/README.md index bef3ad2..232123f 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,6 @@ # IPFS GeoIP -> *Proof of concept:* Geoip lookup over ipfs - +> Geoip lookup over ipfs ## API @@ -33,10 +32,10 @@ a `formatted` property that looks like this: `Mountain View, CA, United States, The utility geoip-gen reads csv files provided from GeoLite, and turns them into a 32-way branching b-tree, which is stored as ipfs json objects. -There is a generator included, that can be called like this: +There is a generator included, that can be run with ```bash -$ node geoip-gen.js path/GeoLite-Blocks.csv path/GeoLite-Location.csv +$ npm run generate ``` This takes quite a long time to import, but you only need to do it once globally to use the lookup feature. @@ -62,6 +61,10 @@ Result: { Pretty result: Mountain View, CA, United States, Earth ``` +## Root hash + +The current root hash for lookups is `QmRn43NNNBEibc6m7zVNcS6UusB1u3qTTfyoLmkugbeeGJ`. + ## License [MIT](LICENSE) diff --git a/bin/generate b/bin/generate new file mode 100755 index 0000000..6d4962c --- /dev/null +++ b/bin/generate @@ -0,0 +1,51 @@ +#!/usr/bin/env node +'use strict' + +const Gauge = require('gauge') +const gen = require('../generate') +const API = require('ipfs-api') + +function handleNoApi () { + console.error('No ipfs daemon running. Please start one') + process.exit(1) +} + +// -- CLI interaction + +const ipfs = new API() + +ipfs.id() + .then((id) => { + if (!id) handleNoApi() + }, handleNoApi) + .then(() => { + const gauge = new Gauge() + let length = 0 + let counter = 0 + + gen.progress.on('progress', (event) => { + if (event.type === 'node') { + length = event.length + } + + if (event.type === 'put') { + counter++ + gauge.show('Uploading', (counter / (length / 32))) + } + + if (event.status === 'start' && event.type !== 'put') { + gauge.show(event.type) + } + }) + + gauge.show('Starting', 0.0001) + return gen.main(ipfs) + }) + .then((hash) => { + console.log('Finished with root hash %s', hash) + process.exit(0) + }) + .catch((err) => { + console.error(err.stack) + process.exit(1) + }) diff --git a/country_data/countries.csv b/country_data/countries.csv deleted file mode 100644 index c74f8db..0000000 --- a/country_data/countries.csv +++ /dev/null @@ -1,260 +0,0 @@ -name,alpha2,countryCallingCodes,alpha3,ioc,currencies,languages,ccTLD,status -Ascension Island,AC,+247,,SHP,USD,eng,.ac,reserved -Andorra,AD,+376,AND,AND,EUR,cat,,assigned -United Arab Emirates,AE,+971,ARE,UAE,AED,ara,,assigned -Afghanistan,AF,+93,AFG,AFG,AFN,pus,,assigned -Antigua And Barbuda,AG,+1 268,ATG,ANT,XCD,eng,,assigned -Anguilla,AI,+1 264,AIA,,XCD,eng,,assigned -Albania,AL,+355,ALB,ALB,ALL,alb,,assigned -Armenia,AM,+374,ARM,ARM,AMD,"arm,rus",,assigned -Angola,AO,+244,AGO,ANG,AOA,por,,assigned -Antarctica,AQ,+672,ATA,,,,,assigned -Argentina,AR,+54,ARG,ARG,ARS,spa,,assigned -American Samoa,AS,+1 684,ASM,ASA,USD,"eng,smo",,assigned -Austria,AT,+43,AUT,AUT,EUR,ger,,assigned -Australia,AU,+61,AUS,AUS,AUD,eng,,assigned -Aruba,AW,+297,ABW,ARU,AWG,dut,,assigned -Åland Islands,AX,+358,ALA,,EUR,swe,,assigned -Azerbaijan,AZ,+994,AZE,AZE,AZN,aze,,assigned -Bosnia & Herzegovina,BA,+387,BIH,BIH,BAM,"bos,cre,srp",,assigned -Barbados,BB,+1 246,BRB,BAR,BBD,eng,,assigned -Bangladesh,BD,+880,BGD,BAN,BDT,ben,,assigned -Belgium,BE,+32,BEL,BEL,EUR,"dut,fre,ger",,assigned -Burkina Faso,BF,+226,BFA,BUR,XOF,fre,,assigned -Bulgaria,BG,+359,BGR,BUL,BGN,bul,,assigned -Bahrain,BH,+973,BHR,BRN,BHD,ara,,assigned -Burundi,BI,+257,BDI,BDI,BIF,fre,,assigned -Benin,BJ,+229,BEN,BEN,XOF,fre,,assigned -Saint Barthélemy,BL,+590,BLM,,EUR,fre,,assigned -Bermuda,BM,+1 441,BMU,BER,BMD,eng,,assigned -Brunei Darussalam,BN,+673,BRN,BRU,BND,"may,eng",,assigned -"Bolivia, Plurinational State Of",BO,+591,BOL,BOL,"BOB,BOV","spa,aym,que",,assigned -"Bonaire, Saint Eustatius And Saba",BQ,+599,BES,,USD,dut,,assigned -Brazil,BR,+55,BRA,BRA,BRL,por,,assigned -Bahamas,BS,+1 242,BHS,BAH,BSD,eng,,assigned -Bhutan,BT,+975,BTN,BHU,"INR,BTN",dzo,,assigned -Bouvet Island,BV,,BVT,,NOK,,,assigned -Botswana,BW,+267,BWA,BOT,BWP,"eng,tsn",,assigned -Belarus,BY,+375,BLR,BLR,BYR,"bel,rus",,assigned -Belize,BZ,+501,BLZ,BIZ,BZD,eng,,assigned -Canada,CA,+1,CAN,CAN,CAD,"eng,fre",,assigned -Cocos (Keeling) Islands,CC,+61,CCK,,AUD,eng,,assigned -Democratic Republic Of Congo,CD,+243,COD,COD,CDF,"fre,lin,kon,swa",,assigned -Central African Republic,CF,+236,CAF,CAF,XAF,"fre,sag",,assigned -Republic Of Congo,CG,+242,COG,CGO,XAF,"fre,lin",,assigned -Switzerland,CH,+41,CHE,SUI,"CHF,CHE,CHW","ger,fre,ita,roh",,assigned -Cote d'Ivoire,CI,+225,CIV,CIV,XOF,fre,,assigned -Cook Islands,CK,+682,COK,COK,NZD,"eng,mao",,assigned -Chile,CL,+56,CHL,CHI,"CLP,CLF",spa,,assigned -Cameroon,CM,+237,CMR,CMR,XAF,"eng,fre",,assigned -China,CN,+86,CHN,CHN,CNY,chi,,assigned -Colombia,CO,+57,COL,COL,"COP,COU",spa,,assigned -Clipperton Island,CP,,,,EUR,,,reserved -Costa Rica,CR,+506,CRI,CRC,CRC,spa,,assigned -Cuba,CU,+53,CUB,CUB,"CUP,CUC",spa,,assigned -Cabo Verde,CV,+238,CPV,CPV,CVE,por,,assigned -Curacao,CW,+599,CUW,,ANG,dut,,assigned -Christmas Island,CX,+61,CXR,,AUD,eng,,assigned -Cyprus,CY,+357,CYP,CYP,EUR,"gre,tur",,assigned -Czech Republic,CZ,+420,CZE,CZE,CZK,cze,,assigned -Germany,DE,+49,DEU,GER,EUR,ger,,assigned -Diego Garcia,DG,,,,USD,,,reserved -Djibouti,DJ,+253,DJI,DJI,DJF,"ara,fre",,assigned -Denmark,DK,+45,DNK,DEN,DKK,dan,,assigned -Dominica,DM,+1 767,DMA,DMA,XCD,eng,,assigned -Dominican Republic,DO,"+1 809,+1 829,+1 849",DOM,DOM,DOP,spa,,assigned -Algeria,DZ,+213,DZA,ALG,DZD,ara,,assigned -"Ceuta, Mulilla",EA,,,,EUR,,,reserved -Ecuador,EC,+593,ECU,ECU,USD,"spa,que",,assigned -Estonia,EE,+372,EST,EST,EUR,est,,assigned -Egypt,EG,+20,EGY,EGY,EGP,ara,,assigned -Western Sahara,EH,+212,ESH,,MAD,,,assigned -Eritrea,ER,+291,ERI,ERI,ERN,"eng,ara,tir",,assigned -Spain,ES,+34,ESP,ESP,EUR,spa,,assigned -Ethiopia,ET,+251,ETH,ETH,ETB,amh,,assigned -European Union,EU,+388,,,EUR,,.eu,reserved -Finland,FI,+358,FIN,FIN,EUR,"fin,swe",,assigned -Fiji,FJ,+679,FJI,FIJ,FJD,"eng,fij",,assigned -Falkland Islands,FK,+500,FLK,,FKP,eng,,assigned -"Micronesia, Federated States Of",FM,+691,FSM,,USD,eng,,assigned -Faroe Islands,FO,+298,FRO,FAI,DKK,"fao,dan",,assigned -France,FR,+33,FRA,FRA,EUR,fre,,assigned -"France, Metropolitan",FX,+241,,,EUR,fre,,reserved -Gabon,GA,+241,GAB,GAB,XAF,fre,,assigned -United Kingdom,GB,+44,GBR,GBR,GBP,"eng,cor,gle,gla,wel",,assigned -Grenada,GD,+473,GRD,GRN,XCD,eng,,assigned -Georgia,GE,+995,GEO,GEO,GEL,geo,,assigned -French Guiana,GF,+594,GUF,,EUR,fre,,assigned -Guernsey,GG,+44,GGY,GCI,GBP,fre,,assigned -Ghana,GH,+233,GHA,GHA,GHS,eng,,assigned -Gibraltar,GI,+350,GIB,,GIP,eng,,assigned -Greenland,GL,+299,GRL,,DKK,kal,,assigned -Gambia,GM,+220,GMB,GAM,GMD,eng,,assigned -Guinea,GN,+224,GIN,GUI,GNF,fre,,assigned -Guadeloupe,GP,+590,GLP,,EUR,fre,,assigned -Equatorial Guinea,GQ,+240,GNQ,GEQ,XAF,"spa,fre,por",,assigned -Greece,GR,+30,GRC,GRE,EUR,gre,,assigned -South Georgia And The South Sandwich Islands,GS,,SGS,,GBP,eng,,assigned -Guatemala,GT,+502,GTM,GUA,GTQ,spa,,assigned -Guam,GU,+1 671,GUM,GUM,USD,eng,,assigned -Guinea-bissau,GW,+245,GNB,GBS,XOF,por,,assigned -Guyana,GY,+592,GUY,GUY,GYD,eng,,assigned -Hong Kong,HK,+852,HKG,HKG,HKD,"chi,eng",,assigned -Heard Island And McDonald Islands,HM,,HMD,,AUD,,,assigned -Honduras,HN,+504,HND,HON,HNL,spa,,assigned -Croatia,HR,+385,HRV,CRO,HRK,hrv,,assigned -Haiti,HT,+509,HTI,HAI,"HTG,USD","fre,hat",,assigned -Hungary,HU,+36,HUN,HUN,HUF,hun,,assigned -Canary Islands,IC,,,,EUR,,,reserved -Indonesia,ID,+62,IDN,INA,IDR,ind,,assigned -Ireland,IE,+353,IRL,IRL,EUR,"eng,gle",,assigned -Israel,IL,+972,ISR,ISR,ILS,"heb,ara,eng",,assigned -Isle Of Man,IM,+44,IMN,,GBP,"eng,glv",,assigned -India,IN,+91,IND,IND,INR,"eng,hin",,assigned -British Indian Ocean Territory,IO,+246,IOT,,USD,eng,,assigned -Iraq,IQ,+964,IRQ,IRQ,IQD,"ara,kur",,assigned -"Iran, Islamic Republic Of",IR,+98,IRN,IRI,IRR,per,,assigned -Iceland,IS,+354,ISL,ISL,ISK,ice,,assigned -Italy,IT,+39,ITA,ITA,EUR,ita,,assigned -Jersey,JE,+44,JEY,JCI,GBP,"eng,fre",,assigned -Jamaica,JM,+1 876,JAM,JAM,JMD,eng,,assigned -Jordan,JO,+962,JOR,JOR,JOD,ara,,assigned -Japan,JP,+81,JPN,JPN,JPY,jpn,,assigned -Kenya,KE,+254,KEN,KEN,KES,"eng,swa",,assigned -Kyrgyzstan,KG,+996,KGZ,KGZ,KGS,rus,,assigned -Cambodia,KH,+855,KHM,CAM,KHR,khm,,assigned -Kiribati,KI,+686,KIR,KIR,AUD,eng,,assigned -Comoros,KM,+269,COM,COM,KMF,"ara,fre",,assigned -Saint Kitts And Nevis,KN,+1 869,KNA,SKN,XCD,eng,,assigned -"Korea, Democratic People's Republic Of",KP,+850,PRK,PRK,KPW,kor,,assigned -"Korea, Republic Of",KR,+82,KOR,KOR,KRW,kor,,assigned -Kuwait,KW,+965,KWT,KUW,KWD,ara,,assigned -Cayman Islands,KY,+1 345,CYM,CAY,KYD,eng,,assigned -Kazakhstan,KZ,"+7,+7 6,+7 7",KAZ,KAZ,KZT,"kaz,rus",,assigned -Lao People's Democratic Republic,LA,+856,LAO,LAO,LAK,lao,,assigned -Lebanon,LB,+961,LBN,LIB,LBP,"ara,arm",,assigned -Saint Lucia,LC,+1 758,LCA,LCA,XCD,eng,,assigned -Liechtenstein,LI,+423,LIE,LIE,CHF,ger,,assigned -Sri Lanka,LK,+94,LKA,SRI,LKR,"sin,tam",,assigned -Liberia,LR,+231,LBR,LBR,LRD,eng,,assigned -Lesotho,LS,+266,LSO,LES,"LSL,ZAR","eng,sot",,assigned -Lithuania,LT,+370,LTU,LTU,EUR,lit,,assigned -Luxembourg,LU,+352,LUX,LUX,EUR,"fre,ger,ltz",,assigned -Latvia,LV,+371,LVA,LAT,EUR,lav,,assigned -Libya,LY,+218,LBY,LBA,LYD,ara,,assigned -Morocco,MA,+212,MAR,MAR,MAD,ara,,assigned -Monaco,MC,+377,MCO,MON,EUR,fre,,assigned -Moldova,MD,+373,MDA,MDA,MDL,rum,,assigned -Montenegro,ME,+382,MNE,MNE,EUR,mot,,assigned -Saint Martin,MF,+590,MAF,,EUR,fre,,assigned -Madagascar,MG,+261,MDG,MAD,MGA,"fre,mlg",,assigned -Marshall Islands,MH,+692,MHL,MHL,USD,"eng,mah",,assigned -"Macedonia, The Former Yugoslav Republic Of",MK,+389,MKD,MKD,MKD,mac,,assigned -Mali,ML,+223,MLI,MLI,XOF,fre,,assigned -Myanmar,MM,+95,MMR,MYA,MMK,bur,,assigned -Mongolia,MN,+976,MNG,MGL,MNT,mon,,assigned -Macao,MO,+853,MAC,MAC,MOP,"chi,por",,assigned -Northern Mariana Islands,MP,+1 670,MNP,,USD,eng,,assigned -Martinique,MQ,+596,MTQ,,EUR,,,assigned -Mauritania,MR,+222,MRT,MTN,MRO,"ara,fre",,assigned -Montserrat,MS,+1 664,MSR,,XCD,,,assigned -Malta,MT,+356,MLT,MLT,EUR,"mlt,eng",,assigned -Mauritius,MU,+230,MUS,MRI,MUR,"eng,fre",,assigned -Maldives,MV,+960,MDV,MDV,MVR,div,,assigned -Malawi,MW,+265,MWI,MAW,MWK,"eng,nya",,assigned -Mexico,MX,+52,MEX,MEX,"MXN,MXV",spa,,assigned -Malaysia,MY,+60,MYS,MAS,MYR,"msa,eng",,assigned -Mozambique,MZ,+258,MOZ,MOZ,MZN,por,,assigned -Namibia,NA,+264,NAM,NAM,"NAD,ZAR",eng,,assigned -New Caledonia,NC,+687,NCL,,XPF,fre,,assigned -Niger,NE,+227,NER,NIG,XOF,fre,,assigned -Norfolk Island,NF,+672,NFK,,AUD,eng,,assigned -Nigeria,NG,+234,NGA,NGR,NGN,eng,,assigned -Nicaragua,NI,+505,NIC,NCA,NIO,spa,,assigned -Netherlands,NL,+31,NLD,NED,EUR,dut,,assigned -Norway,NO,+47,NOR,NOR,NOK,nor,,assigned -Nepal,NP,+977,NPL,NEP,NPR,nep,,assigned -Nauru,NR,+674,NRU,NRU,AUD,"eng,nau",,assigned -Niue,NU,+683,NIU,,NZD,eng,,assigned -New Zealand,NZ,+64,NZL,NZL,NZD,eng,,assigned -Oman,OM,+968,OMN,OMA,OMR,ara,,assigned -Panama,PA,+507,PAN,PAN,"PAB,USD",spa,,assigned -Peru,PE,+51,PER,PER,PEN,"spa,aym,que",,assigned -French Polynesia,PF,+689,PYF,,XPF,fre,,assigned -Papua New Guinea,PG,+675,PNG,PNG,PGK,eng,,assigned -Philippines,PH,+63,PHL,PHI,PHP,eng,,assigned -Pakistan,PK,+92,PAK,PAK,PKR,"urd,eng",,assigned -Poland,PL,+48,POL,POL,PLN,pol,,assigned -Saint Pierre And Miquelon,PM,+508,SPM,,EUR,eng,,assigned -Pitcairn,PN,+872,PCN,,NZD,eng,,assigned -Puerto Rico,PR,"+1 787,+1 939",PRI,PUR,USD,"spa,eng",,assigned -"Palestinian Territory, Occupied",PS,+970,PSE,PLE,"JOD,EGP,ILS",ara,,assigned -Portugal,PT,+351,PRT,POR,EUR,por,,assigned -Palau,PW,+680,PLW,PLW,USD,eng,,assigned -Paraguay,PY,+595,PRY,PAR,PYG,spa,,assigned -Qatar,QA,+974,QAT,QAT,QAR,ara,,assigned -Reunion,RE,+262,REU,,EUR,fre,,assigned -Romania,RO,+40,ROU,ROU,RON,rum,,assigned -Serbia,RS,+381,SRB,SRB,RSD,srp,,assigned -Russian Federation,RU,"+7,+7 3,+7 4,+7 8",RUS,RUS,RUB,rus,,assigned -Rwanda,RW,+250,RWA,RWA,RWF,"eng,fre,kin",,assigned -Saudi Arabia,SA,+966,SAU,KSA,SAR,ara,,assigned -Solomon Islands,SB,+677,SLB,SOL,SBD,eng,,assigned -Seychelles,SC,+248,SYC,SEY,SCR,"eng,fre",,assigned -Sudan,SD,+249,SDN,SUD,SDG,"ara,eng",,assigned -Sweden,SE,+46,SWE,SWE,SEK,swe,,assigned -Singapore,SG,+65,SGP,SIN,SGD,"eng,chi,may,tam",,assigned -"Saint Helena, Ascension And Tristan Da Cunha",SH,+290,SHN,,SHP,eng,,assigned -Slovenia,SI,+386,SVN,SLO,EUR,slv,,assigned -Svalbard And Jan Mayen,SJ,+47,SJM,,NOK,,,assigned -Slovakia,SK,+421,SVK,SVK,EUR,slo,,assigned -Sierra Leone,SL,+232,SLE,SLE,SLL,eng,,assigned -San Marino,SM,+378,SMR,SMR,EUR,ita,,assigned -Senegal,SN,+221,SEN,SEN,XOF,fre,,assigned -Somalia,SO,+252,SOM,SOM,SOS,som,,assigned -Suriname,SR,+597,SUR,SUR,SRD,dut,,assigned -South Sudan,SS,+211,SSD,,SSP,eng,.ss,assigned -São Tomé and Príncipe,ST,+239,STP,STP,STD,por,,assigned -USSR,SU,,,,RUB,rus,.su,reserved -El Salvador,SV,+503,SLV,ESA,USD,spa,,assigned -Sint Maarten,SX,+1 721,SXM,,ANG,dut,,assigned -Syrian Arab Republic,SY,+963,SYR,SYR,SYP,ara,,assigned -Swaziland,SZ,+268,SWZ,SWZ,SZL,"eng,ssw",,assigned -Turks And Caicos Islands,TC,+1 649,TCA,,USD,eng,,assigned -Tristan de Cunha,TA,+290,,,GBP,,,reserved -Chad,TD,+235,TCD,CHA,XAF,"ara,fre",,assigned -French Southern Territories,TF,,ATF,,EUR,fre,,assigned -Togo,TG,+228,TGO,TOG,XOF,fre,,assigned -Thailand,TH,+66,THA,THA,THB,tha,,assigned -Tajikistan,TJ,+992,TJK,TJK,TJS,"tgk,rus",,assigned -Tokelau,TK,+690,TKL,,NZD,eng,,assigned -East Timor,TL,+670,TLS,TLS,USD,por,,assigned -Turkmenistan,TM,+993,TKM,TKM,TMT,"tuk,rus",,assigned -Tunisia,TN,+216,TUN,TUN,TND,ara,,assigned -Tonga,TO,+676,TON,TGA,TOP,eng,,assigned -Turkey,TR,+90,TUR,TUR,TRY,tur,,assigned -Trinidad And Tobago,TT,+1 868,TTO,TRI,TTD,eng,,assigned -Tuvalu,TV,+688,TUV,TUV,AUD,eng,,assigned -"Taiwan, Province Of China",TW,+886,TWN,TPE,TWD,chi,,assigned -"Tanzania, United Republic Of",TZ,+255,TZA,TAN,TZS,"swa,eng",,assigned -Ukraine,UA,+380,UKR,UKR,UAH,"ukr,rus",,assigned -Uganda,UG,+256,UGA,UGA,UGX,"eng,swa",,assigned -United States Minor Outlying Islands,UM,+1,UMI,,USD,eng,,assigned -United Kingdom,UK,,,,GBP,"eng,cor,gle,gla,wel",.uk,reserved -United States,US,+1,USA,USA,USD,eng,,assigned -Uruguay,UY,+598,URY,URU,"UYU,UYI",spa,,assigned -Uzbekistan,UZ,+998,UZB,UZB,UZS,"uzb,rus",,assigned -Vatican City State,VA,"+379,+39",VAT,,EUR,ita,,assigned -Saint Vincent And The Grenadines,VC,+1 784,VCT,VIN,XCD,eng,,assigned -"Venezuela, Bolivarian Republic Of",VE,+58,VEN,VEN,VEF,spa,,assigned -Virgin Islands (British),VG,+1 284,VGB,ISV,USD,eng,,assigned -Virgin Islands (US),VI,+1 340,VIR,ISV,USD,eng,,assigned -Viet Nam,VN,+84,VNM,VIE,VND,vie,,assigned -Vanuatu,VU,+678,VUT,VAN,VUV,"bis,eng,fre",,assigned -Wallis And Futuna,WF,+681,WLF,,XPF,fre,,assigned -Samoa,WS,+685,WSM,SAM,WST,"eng,smo",,assigned -Yemen,YE,+967,YEM,YEM,YER,ara,,assigned -Mayotte,YT,+262,MYT,,EUR,fre,,assigned -South Africa,ZA,+27,ZAF,RSA,ZAR,"afr,eng,nbl,som,tso,ven,xho,zul",,assigned -Zambia,ZM,+260,ZMB,ZAM,ZMW,eng,,assigned -Zimbabwe,ZW,+263,ZWE,ZIM,"USD,ZAR,BWP,GBP,EUR","eng,sna,nde",,assigned \ No newline at end of file diff --git a/example/lookup.js b/example/lookup.js index 168022d..831699b 100644 --- a/example/lookup.js +++ b/example/lookup.js @@ -1,14 +1,14 @@ 'use strict' -var geoip = require('../') -var ipfs = require('ipfs-api')() +const geoip = require('../') +const ipfs = require('ipfs-api')() if (process.argv.length !== 3) { console.log('usage: node lookup.js ') process.exit(1) } -geoip.lookup(ipfs, process.argv[2], function (err, result) { +geoip.lookup(ipfs, process.argv[2], (err, result) => { if (err) { console.log('Error: ' + err) } else { @@ -16,7 +16,7 @@ geoip.lookup(ipfs, process.argv[2], function (err, result) { } }) -geoip.lookupPretty(ipfs, '/ip4/' + process.argv[2], function (err, result) { +geoip.lookupPretty(ipfs, '/ip4/' + process.argv[2], (err, result) => { if (err) { console.log('Error: ' + err) } else { diff --git a/generate/geoip-gen.js b/generate/geoip-gen.js deleted file mode 100644 index 90f48af..0000000 --- a/generate/geoip-gen.js +++ /dev/null @@ -1,200 +0,0 @@ -'use strict' - -var ipfs = require('ipfs-api')() -var csv = require('csv') -var fs = require('fs') -var Q = require('kew') -var iconv = require('iconv-lite') - -var CHILDREN = 32 - -function parseCountries (file) { - var def = Q.defer() - var countries = {} - - fs.readFile(file, function (err, data) { - if (err) throw err - csv.parse(data, function (err, parsed) { - if (err) throw err - - parsed.forEach(function (row) { - countries[row[1]] = row[0] - }) - def.resolve(countries) - }) - }) - - return def.promise -} - -function parseLocations (file, countries) { - var def = Q.defer() - var locations = {} - - fs.readFile(file, function (err, data) { - if (err) throw err - - csv.parse(iconv.decode(data, 'latin1'), function (err, parsed) { - if (err) throw err - - parsed.forEach(function (row) { - var locid = parseInt(row[0], 10) - var data = row - data[0] = countries[row[1]] - locations[locid] = data - }) - console.log('parsed locations') - def.resolve(locations) - }) - }) - - return def.promise -} - -var ENTRY_COUNT = 0 - -function parseBlocks (file, locations) { - var def = Q.defer() - var entries = [] - - fs.readFile(file, function (err, rawdata) { - if (err) throw err - csv.parse(rawdata, function (err, parsed) { - if (err) throw err - var last_end = 0 - parsed.forEach(function (row) { - var start = parseInt(row[0], 10) - var end = parseInt(row[1], 10) - var locid = parseInt(row[2], 10) - - // unmapped range? - if ((start - last_end) > 1) { - ENTRY_COUNT++ - entries.push({min: last_end + 1, - data: 0}) - } - - ENTRY_COUNT++ - entries.push({min: start, - data: locations[locid]}) - - last_end = end - }) - console.log('parsed blocks') - def.resolve(entries) - }) - }) - - return def.promise -} - -var PROGRESS = 0 -// we need a queue not to hammer the api with a gazillion -// requests at the same time -var Queue = Q.resolve() - -function putObject (data, min, def) { - Queue = Queue.then(function () { - ipfs.object.put(data, 'json', function (err, put) { - if (err || !put) { - console.log('error in put:') - console.log(err, put) - } - - ipfs.object.stat(put.Hash, function (err, stat) { - if (err || !stat) { - console.log('error in stat:') - console.log(err, stat) - } - - PROGRESS++ - console.log('approx progress: ' + - (((PROGRESS / (ENTRY_COUNT / 32)) * 100) + '').substr(0, 4) + '%') - - def.resolve({min: min, - size: stat.CumulativeSize, - hash: put.Hash}) - }) - }) - }) - - Queue = Queue.delay(20) -} - -function toNode (things) { - var def = Q.defer() - var length = things.length - - if (length <= CHILDREN) { - var min = things[0].min - var data - - if (!things[0].hash) { - // btree leaf - var leaf = JSON.stringify({type: 'Leaf', - data: things}) - - data = new Buffer(JSON.stringify({Data: leaf})) - } else { - // btree node - var node = JSON.stringify({type: 'Node', - mins: things.map(function (x) { return x.min })}) - - data = new Buffer(JSON.stringify({Data: node, - Links: things.map(function (x) { - return {Hash: x.hash, - Size: x.size} - })})) - } - putObject(data, min, def) - } else { - // divide - - var promises = [] - var pointer = 0 - - while (pointer < length) { - console.log(pointer / length) - promises.push( - toNode(things.slice(pointer, pointer + CHILDREN))) - pointer += CHILDREN - } - - Q.all(promises) - .then(function (results) { - toNode(results).then(function (result) { - // done! - def.resolve(result) - }) - }) - } - - return def.promise -} - -function usage () { - console.log('usage: node geoip-gen.js blockfile.csv locationfile.csv') - process.exit(1) -} - -function main () { - parseCountries(process.cwd() + '/../country_data/countries.csv') - .then(function (countries) { - parseLocations(process.cwd() + '/' + process.argv[3], countries) - .then(function (locations) { - parseBlocks(process.cwd() + '/' + process.argv[2], locations) - .then(function (entries) { - toNode(entries).then(function (result) { - console.log('done!') - console.log(result.hash) - }) - }) - }) - }) -} - -if (process.argv.length !== 4) { - usage() -} else { - main() -} diff --git a/generate/index.js b/generate/index.js new file mode 100644 index 0000000..30d7baf --- /dev/null +++ b/generate/index.js @@ -0,0 +1,232 @@ +'use strict' + +const Promise = require('bluebird') +const csv = Promise.promisifyAll(require('csv')) +const iconv = require('iconv-lite') +const _ = require('lodash') +const EventEmitter = require('events').EventEmitter +const bl = require('bl') + +// Btree size +const CHILDREN = 32 + +// All data is stored in an ipfs folder called data +// this is the hash of that folder. It includes three files +// +// data +// |- blocks.csv +// |- countries.csv +// |- locations.csv +const DATA_HASH = 'QmTMh5Q1CnB9jV774aKCvPSqibwDy9sJmo7BCThD5f1oY3' + +const progress = new EventEmitter() + +function emit (type, status, attrs) { + progress.emit('progress', Object.assign({}, { + type: type, + status: status + }, attrs)) +} + +function parseCountries (countries) { + emit('countries', 'start') + return csv.parseAsync(countries.toString(), { + columns: true, + skip_empty_lines: true + }) + .then((parsed) => { + return _.reduce(parsed, (acc, row) => { + acc[row.alpha2] = row.name + return acc + }, {}) + }) + .then((result) => { + emit('countries', 'end') + return result + }) +} + +function parseLocations (locations, countries) { + emit('locations', 'start') + return csv.parseAsync(iconv.decode(locations, 'latin1'), { + columns: true, + auto_parse: true, + skip_empty_lines: true, + comment: '#' + }) + .then((parsed) => { + return _.reduce(parsed, (acc, row) => { + acc[row.locId] = [ + countries[row.country], + row.country, + row.region, + row.city, + row.postalCode, + row.latitude, + row.longitude, + row.metroCode, + row.areaCode + ] + return acc + }, {}) + }) + .then((result) => { + emit('locations', 'end') + return result + }) +} + +function parseBlocks (blocks, locations) { + emit('blocks', 'start') + return csv.parseAsync(blocks.toString(), { + columns: true, + auto_parse: true, + skip_empty_lines: true, + comment: '#' + }) + .then((parsed) => { + var last_end = 0 + + return _.reduce(parsed, (acc, row) => { + var start = row.startIpNum + var end = row.endIpNum + var locid = row.locId + + // unmapped range? + if ((start - last_end) > 1) { + acc.push({ + min: last_end + 1, + data: 0 + }) + } + + acc.push({ + min: start, + data: locations[locid] + }) + + last_end = end + + return acc + }, []) + }) + .then((result) => { + emit('blocks', 'end') + return result + }) +} + +function putObject (data, min, api) { + return api.object.put(data, 'json') + .then((put) => { + return api.object.stat(put.Hash) + .then((stat) => { + if (!stat) { + throw new Error(`Could not stat object ${put.Hash}`) + } + emit('put', 'end') + return { + min: min, + size: stat.CumulativeSize, + hash: put.Hash + } + }) + }) +} + +// Create a btree leaf with data +function createLeaf (data) { + return new Buffer(JSON.stringify({ + Data: JSON.stringify({ + type: 'Leaf', + data: data + }) + })) +} +// Create a btree node with data +function createNode (data) { + return new Buffer(JSON.stringify({ + Data: JSON.stringify({ + type: 'Node', + mins: data.map((x) => x.min) + }), + Links: data.map((x) => ({ + Hash: x.hash, + Size: x.size + })) + })) +} + +function toNode (things, api) { + const length = things.length + + if (length <= CHILDREN) { + const first = things[0] + const min = first.min + + if (!first.hash) { + return putObject(createLeaf(things), min, api) + } + + return putObject(createNode(things), min, api) + } + + // divide + return Promise.map(_.chunk(things, CHILDREN), (res) => toNode(res, api), { + concurrency: 5 + }) + .then((res) => toNode(res, api)) +} + +function file (ipfs, dir) { + return ipfs.cat(`${DATA_HASH}/${dir}`) + .then((buffer) => { + return new Promise((resolve, reject) => { + buffer.pipe(bl((err, data) => { + if (err) return reject(err) + resolve(data) + })) + }) + }) +} + +function main (ipfs) { + return file(ipfs, 'countries.csv') + .then(parseCountries) + .then((countries) => Promise.join( + file(ipfs, 'locations.csv'), + countries, + parseLocations + )) + .then((locations) => Promise.join( + file(ipfs, 'blocks.csv'), + locations, + parseBlocks + )) + .then((result) => { + emit('node', 'start', { + length: result.length + }) + + return toNode(result, ipfs) + }) + .then((result) => { + emit('node', 'end') + emit('pinning', 'start') + return ipfs.pin.add(result.hash, {recursive: true}) + }) + .then((result) => { + emit('pinning', 'end') + return result.Pinned[0] + }) +} + +module.exports = { + parseCountries: parseCountries, + parseLocations: parseLocations, + parseBlocks: parseBlocks, + putObject: putObject, + toNode: toNode, + main: main, + progress: progress +} diff --git a/index.js b/index.js new file mode 100644 index 0000000..cf741f8 --- /dev/null +++ b/index.js @@ -0,0 +1,9 @@ +'use strict' + +const lookup = require('./lib/lookup') +const lookupPretty = require('./lib/pretty') + +module.exports = { + lookup: lookup, + lookupPretty: lookupPretty +} diff --git a/ipfs-geoip.js b/ipfs-geoip.js deleted file mode 100644 index 2e43369..0000000 --- a/ipfs-geoip.js +++ /dev/null @@ -1,11 +0,0 @@ -'use strict' - -var l = require('./lib/lookup') -var lookupPretty = require('./lib/pretty') - -module.exports = { - lookup: l.lookup, - lookup_root: l.lookup_root, - _lookup: l._lookup, - lookupPretty: lookupPretty -} diff --git a/lib/aton4.js b/lib/aton4.js deleted file mode 100644 index 24da14a..0000000 --- a/lib/aton4.js +++ /dev/null @@ -1,10 +0,0 @@ -'use strict' - -module.exports = function aton4 (a) { - a = a.split(/\./) - - return ((parseInt(a[0], 10) << 24) >>> 0) + - ((parseInt(a[1], 10) << 16) >>> 0) + - ((parseInt(a[2], 10) << 8) >>> 0) + - (parseInt(a[3], 10) >>> 0) -} diff --git a/lib/format.js b/lib/format.js index edb47a9..00513f3 100644 --- a/lib/format.js +++ b/lib/format.js @@ -1,22 +1,19 @@ 'use strict' // TODO(dignifiedquire): Adjust for more planets -var PLANET = 'Earth' +const PLANET = 'Earth' module.exports = function formatData (data) { - var obj = {} - - if (data[0]) obj.country_name = data[0] - if (data[1]) obj.country_code = data[1] - if (data[2]) obj.region_code = data[2] - if (data[3]) obj.city = data[3] - if (data[4]) obj.postal_code = data[4] - if (data[5]) obj.latitude = parseFloat(data[5]) - if (data[6]) obj.longitude = parseFloat(data[6]) - if (data[7]) obj.metro_code = data[7] - if (data[8]) obj.area_code = data[8] - - obj.planet = PLANET - - return obj + return { + country_name: data[0], + country_code: data[1], + region_code: data[2], + city: data[3], + postal_code: data[4], + latitude: data[5], + longitude: data[6], + metro_code: data[7], + area_code: data[8], + planet: PLANET + } } diff --git a/lib/lookup.js b/lib/lookup.js index d78b0e9..f9d2d51 100644 --- a/lib/lookup.js +++ b/lib/lookup.js @@ -1,60 +1,61 @@ 'use strict' -var memoize = require('memoizee') +const memoize = require('memoizee') +const inet = require('inet_ipv4') -var formatData = require('./format') -var aton4 = require('./aton4') +const formatData = require('./format') -var GEOIP_ROOT = 'QmQQ3BUpPjgYiTdhp4H9YWSCtoFXs8t91njhpvXNNLd3yB' +const GEOIP_ROOT = 'QmRn43NNNBEibc6m7zVNcS6UusB1u3qTTfyoLmkugbeeGJ' -var memoized_lookup +let memoized_lookup function _lookup (ipfs, hash, lookfor, cb) { - ipfs.object.get(hash, function (err, res) { - if (err) { - return cb(err, null) - } + ipfs.object.get(hash, (err, res) => { + if (err) return cb(err) + let obj try { - var obj = JSON.parse(res.Data) + obj = JSON.parse(res.Data) } catch (err) { - return cb(err, null) + return cb(err) } - var child = 0 + let child = 0 + if (obj.type === 'Node') { - while (obj.mins[child] && - obj.mins[child] <= lookfor) { + while (obj.mins[child] && obj.mins[child] <= lookfor) { child++ } - return memoized_lookup(ipfs, res.Links[child - 1].Hash, lookfor, cb) + + const next = res.Links[child - 1] + + if (!next || !next.Hash) { + return cb(new Error('Failed to lookup node')) + } + + return memoized_lookup(ipfs, next.Hash, lookfor, cb) } else if (obj.type === 'Leaf') { - while (obj.data[child] && - obj.data[child].min <= lookfor) { + while (obj.data[child] && obj.data[child].min <= lookfor) { child++ } - if (obj.data[child - 1].data) { - cb(null, formatData(obj.data[child - 1].data)) - } else { - cb('Unmapped range', null) + const next = obj.data[child - 1] + + if (!next) { + return cb(new Error('Failed to lookup leaf node')) + } + + if (!next.data) { + return cb(new Error('Unmapped range'), null) } + + return cb(null, formatData(next.data)) } }) } memoized_lookup = memoize(_lookup, {async: true}) -function lookup (ipfs, ip, cb) { - memoized_lookup(ipfs, GEOIP_ROOT, aton4(ip), cb) -} - -function lookup_root (ipfs, hash, ip, cb) { - memoized_lookup(ipfs, hash, aton4(ip), cb) -} - -module.exports = { - lookup: lookup, - lookup_root: lookup_root, - _lookup: memoized_lookup +module.exports = function lookup (ipfs, ip, cb) { + memoized_lookup(ipfs, GEOIP_ROOT, inet.aton(ip), cb) } diff --git a/lib/pretty.js b/lib/pretty.js index c00f4f7..3512e13 100644 --- a/lib/pretty.js +++ b/lib/pretty.js @@ -1,6 +1,6 @@ 'use strict' -var lookup = require('./lookup').lookup +const lookup = require('./lookup') function isLocal (address) { var split = address.split('.') @@ -12,23 +12,32 @@ function isLocal (address) { } module.exports = function lookupPretty (ipfs, multiaddrs, cb) { - if (multiaddrs.length === 0) return cb(null, null) - if (typeof multiaddrs === 'string') multiaddrs = [multiaddrs] + if (multiaddrs.length === 0) { + return cb(new Error('Unmapped range'), null) + } - var current = multiaddrs[0].split('/') - var address = current[2] + if (typeof multiaddrs === 'string') { + multiaddrs = [multiaddrs] + } + + const current = multiaddrs[0].split('/') + const address = current[2] // No ip6 support at the moment - if (isLocal(address) || current[1] === 'ip6') return lookupPretty(ipfs, multiaddrs.slice(1), cb) + if (isLocal(address) || current[1] === 'ip6') { + return lookupPretty(ipfs, multiaddrs.slice(1), cb) + } - lookup(ipfs, address, function (err, res) { - if (err) return cb(err) + lookup(ipfs, address, (err, res) => { + if (err) { + return cb(err) + } if (!res.country_name && multiaddrs.length > 1) { return lookupPretty(ipfs, multiaddrs.slice(1), cb) } - var location = [] + const location = [] if (res.planet) location.push(res.planet) if (res.country_name) location.unshift(res.country_name) diff --git a/package.json b/package.json index 39fd7a2..be731d7 100644 --- a/package.json +++ b/package.json @@ -2,22 +2,30 @@ "name": "ipfs-geoip", "version": "1.4.5", "description": "Library for ipfs geoip lookups", - "main": "ipfs-geoip.js", + "main": "index.js", "dependencies": { + "inet_ipv4": "^1.0.0", "memoizee": "^0.3.8" }, "repository": { "type": "git", - "url": "https://github.com/krl/ipfs-geoip" + "url": "https://github.com/ipfs/ipfs-geoip" }, "devDependencies": { + "bl": "^1.1.2", + "bluebird": "^3.3.1", + "chai": "^3.5.0", + "chai-as-promised": "^5.2.0", "csv": "^0.4.2", "eslint-plugin-react": "^3.5.1", + "gauge": "^1.2.5", "iconv-lite": "^0.4.8", - "ipfs-api": "^2.4.1", - "kew": "^0.7.0", + "ipfs-api": "^2.13.0", + "ipfsd-ctl": "^0.8.1", + "lodash": "^4.5.0", + "mocha": "^2.4.5", "pre-commit": "^1.0.6", - "standard": "^5.3.1" + "standard": "^6.0.5" }, "keywords": [ "ipfs", @@ -26,17 +34,21 @@ "author": "Kristoffer Ström ", "license": "MIT", "bugs": { - "url": "https://github.com/krl/ipfs-geoip/issues" + "url": "https://github.com/ipfs/ipfs-geoip/issues" }, - "homepage": "https://github.com/krl/ipfs-geoip", + "homepage": "https://github.com/ipfs/ipfs-geoip", "directories": { "example": "example" }, "scripts": { - "test": "echo \"Error: no test specified\" && exit 1", - "lint": "node_modules/.bin/standard" + "test": "mocha", + "lint": "node_modules/.bin/standard", + "generate": "bin/generate" }, "pre-commit": [ "lint" - ] + ], + "engines": { + "node": ">=4.0.0" + } } diff --git a/test/format.spec.js b/test/format.spec.js new file mode 100644 index 0000000..cc32a79 --- /dev/null +++ b/test/format.spec.js @@ -0,0 +1,62 @@ +/* eslint-env mocha */ +'use strict' + +const expect = require('chai').expect + +const format = require('../lib/format.js') + +describe('format', () => { + it('formats with all details present', () => { + expect( + format([ + 'United States', + 'US', + 'CA', + 'Mountain View', + 94040, + 37.386, + -122.0838, + 807, + 650 + ]) + ).to.be.eql({ + country_name: 'United States', + country_code: 'US', + region_code: 'CA', + city: 'Mountain View', + postal_code: 94040, + latitude: 37.386, + longitude: -122.0838, + metro_code: 807, + area_code: 650, + planet: 'Earth' + }) + }) + + it('formats with missing details', () => { + expect( + format([ + 'United States', + 'US', + 'CA', + '', + '', + 37.386, + -122.0838, + '', + '' + ]) + ).to.be.eql({ + country_name: 'United States', + country_code: 'US', + region_code: 'CA', + city: '', + postal_code: '', + latitude: 37.386, + longitude: -122.0838, + metro_code: '', + area_code: '', + planet: 'Earth' + }) + }) +}) diff --git a/test/generate.spec.js b/test/generate.spec.js new file mode 100644 index 0000000..996a990 --- /dev/null +++ b/test/generate.spec.js @@ -0,0 +1,133 @@ +/* eslint-env mocha */ +'use strict' + +const chai = require('chai') +const asPromised = require('chai-as-promised') +chai.use(asPromised) +const expect = chai.expect + +const gen = require('../generate/index.js') + +const countries = new Buffer(` +name,alpha2,countryCallingCodes,alpha3,ioc,currencies,languages,ccTLD,status +Ascension Island,AC,+247,,SHP,USD,eng,.ac,reserved +Andorra,AD,+376,AND,AND,EUR,cat,,assigned +United Arab Emirates,AE,+971,ARE,UAE,AED,ara,,assigned +Afghanistan,AF,+93,AFG,AFG,AFN,pus,,assigned +Antigua And Barbuda,AG,+1 268,ATG,ANT,XCD,eng,,assigned +`) + +const locations = new Buffer(` +# Copyright (c) 2012 MaxMind LLC. All Rights Reserved. +locId,country,region,city,postalCode,latitude,longitude,metroCode,areaCode +1,"AD","","","",42.5000,1.5000,, +2,"AE","","","",24.0000,54.0000,, +3,"AF","","","",33.0000,65.0000,, +4,"AG","","","",17.0500,-61.8000,, +`) + +const blocks = new Buffer(` +# Copyright (c) 2011 MaxMind Inc. All Rights Reserved. +startIpNum,endIpNum,locId +"16777216","16777471","1" +"16777472","16778239","2" +"16778240","16779263","3" +"16779264","16781311","4" +`) + +describe('generate', () => { + it('parseCountries', () => { + return expect( + gen.parseCountries(countries) + ).to.eventually.be.eql({ + AC: 'Ascension Island', + AD: 'Andorra', + AE: 'United Arab Emirates', + AF: 'Afghanistan', + AG: 'Antigua And Barbuda' + }) + }) + + it('parseLocations', () => { + return expect( + gen.parseLocations(locations, { + AC: 'Ascension Island', + AD: 'Andorra', + AE: 'United Arab Emirates', + AF: 'Afghanistan', + AG: 'Antigua And Barbuda' + }) + ).to.eventually.be.eql({ + 1: ['Andorra', 'AD', '', '', '', 42.5, 1.5, '', ''], + 2: ['United Arab Emirates', 'AE', '', '', '', 24, 54, '', ''], + 3: ['Afghanistan', 'AF', '', '', '', 33, 65, '', ''], + 4: ['Antigua And Barbuda', 'AG', '', '', '', 17.05, -61.8, '', ''] + }) + }) + + it('parseBlocks', () => { + return expect( + gen.parseBlocks(blocks, { + 1: ['Andorra', 'AD', '', '', '', 42.5, 1.5, '', ''], + 2: ['United Arab Emirates', 'AE', '', '', '', 24, 54, '', ''], + 3: ['Afghanistan', 'AF', '', '', '', 33, 65, '', ''], + 4: ['Antigua And Barbuda', 'AG', '', '', '', 17.05, -61.8, '', ''] + }) + ).to.eventually.be.eql([{ + min: 1, + data: 0 + }, { + min: 16777216, + data: [ 'Andorra', 'AD', '', '', '', 42.5, 1.5, '', '' ] + }, { + min: 16777472, + data: [ 'United Arab Emirates', 'AE', '', '', '', 24, 54, '', '' ] + }, { + min: 16778240, + data: [ 'Afghanistan', 'AF', '', '', '', 33, 65, '', '' ] + }, { + min: 16779264, + data: [ 'Antigua And Barbuda', 'AG', '', '', '', 17.05, -61.8, '', '' ] + }]) + }) + + it('putObject', () => { + const api = { + object: { + put: () => Promise.resolve({Hash: 'myhash'}), + stat: (hash) => Promise.resolve({CumulativeSize: 5}) + } + } + + return expect( + gen.putObject(['hello'], 3, api) + ).to.eventually.be.eql({ + min: 3, + size: 5, + hash: 'myhash' + }) + }) + + it('toNode', () => { + const api = { + object: { + put: (val) => Promise.resolve({Hash: 'myhash' + val.length}), + stat: (hash) => Promise.resolve({CumulativeSize: hash.length}) + } + } + + return expect( + gen.toNode([{ + min: 1, + data: 0 + }, { + min: 16777216, + data: [ 'Andorra', 'AD', '', '', '', 42.5, 1.5, '', '' ] + }], api) + ).to.eventually.be.eql({ + min: 1, + size: 9, + hash: 'myhash147' + }) + }) +}) diff --git a/test/lookup.spec.js b/test/lookup.spec.js new file mode 100644 index 0000000..f2945bd --- /dev/null +++ b/test/lookup.spec.js @@ -0,0 +1,73 @@ +/* eslint-env mocha */ +'use strict' + +const expect = require('chai').expect + +const geoip = require('../') +const IPFS = require('ipfs-api') +const ctl = require('ipfsd-ctl') + +describe('lookup', function () { + this.timeout(20 * 1000) + let ipfs + + before((done) => { + ctl.disposable((err, node) => { + if (err) throw err + + node.startDaemon((err) => { + if (err) throw err + ipfs = IPFS(node.apiAddr) + done() + }) + }) + }) + + it('fails on 127.0.0.1', (done) => { + geoip.lookup(ipfs, '127.0.0.1', function (err, result) { + expect(err).to.have.property('message', 'Unmapped range') + done() + }) + }) + + it('looks up 8.8.8.8', (done) => { + geoip.lookup(ipfs, '8.8.8.8', function (err, result) { + expect(err).to.not.exist + expect( + result + ).to.be.eql({ + country_name: 'United States', + country_code: 'US', + region_code: 'CA', + city: 'Mountain View', + postal_code: 94040, + latitude: 37.386, + longitude: -122.0838, + metro_code: 807, + area_code: 650, + planet: 'Earth' + }) + + done() + }) + }) + + describe('lookupPretty', () => { + it('fails on 127.0.0.1', (done) => { + geoip.lookupPretty(ipfs, '/ip4/127.0.0.1', function (err, result) { + expect(err).to.have.property('message', 'Unmapped range') + done() + }) + }) + + it('looks up 8.8.8.8', (done) => { + geoip.lookupPretty(ipfs, '/ip4/8.8.8.8', function (err, result) { + expect(err).to.not.exist + expect( + result.formatted + ).to.be.eql('Mountain View, CA, United States, Earth') + done() + }) + }) + }) +})