Mon 21 Jul 22:43:21 CEST 2025

This commit is contained in:
sbosse 2025-07-21 23:08:26 +02:00
parent 3d9ecd1714
commit b90dc89bcb

285
js/geoip/geoloc5.js Normal file
View File

@ -0,0 +1,285 @@
var Options = {
// stage1
locate : {
primary : {http:'ag-0.de:9999',https:'ag-0.de:9998',path:'/json',timeout:1000},
secondary : {http:'ip-api.com:80',https:'ip-api.com:80',path:'/json',timeout:2000},
},
// stage2
locate5 : {
// secondary : {https:'api.beacondb.net:443',path:'/v1/geolocate?key=test'},
},
// stage 3: lat,lon -> location info lookup
reverse : {
primary : {https:'api.opencagedata.com',path:'/geocode/v1/json?q=LAT+LON&key=f4280d4685bd4241b2f754957b86dac6'},
secondary: {https:'nominatim.openstreetmap.org',path:'/reverse/?format=json&lat=LAT&lon=LON'},
},
verbose : 0
}
var http=Require("http"),https; /* https on demand */
function ip(url) { return url.split(':')[0] }
function port(url) { return url.split(':')[1] }
// 1. GPS/GEO via IP and external database service
function stage1 (cb,options,location) {
var e,r;
if (!http.xhr && http.request) {
// node.js
e={hostname:ip(options.http),
path:options.path||'',method:"GET",
port:port(options.http)};
if (Options.verbose) console.log('locate.stage1',e);
r=http.request(e,function(a){
a.setEncoding("utf8");
var c="";
a.on("data",function(a){c+=a}),
a.on("end",function(){
try {
var info=JSON.parse(c);
if (Options.verbose) console.log('locate.stage1.res',info);
Object.assign(location,{
ip:info.query,
gps:{lat:info.lat,lon:info.lon},
geo:{city:info.city,country:info.country,countryCode:info.countryCode,region:info.region,zip:info.zip}
})
return cb(location);
} catch (err) {
return cb(err);
}
})
});
r.on("error",function(a) {
if (Options.verbose) console.log('locate.stage1.err',a);
return cb(a);
})
r.setTimeout(options.timeout,function() {
if (Options.verbose) console.log('locate.stage1.err',"ETIMEOUT");
return cb(new Error("ETIMEOUT"));
})
r.end();
} else {
// Browser
var proto=document.URL.indexOf('https')==0?'https':'http';
e={uri:proto+'://'+options[proto]+'/'+options.path,
method:"GET",
headers:{}};
if (Options.verbose) console.log('stage1',e);
http.request(e,function(err,xhr,body){
if (err) {
if (Options.verbose) console.log('locate.stage1.err',err);
return cb(err);
}
try {
var info=JSON.parse(body);
if (Options.verbose) console.log('locate.stage1.res',info);
Object.assign(location,{
ip:info.query,
gps:{lat:info.lat,lon:info.lon},
geo:{city:info.city,country:info.country,countryCode:info.countryCode,region:info.region,zip:info.zip}
})
return cb(location);
} catch (err) {
if (Options.verbose) console.log('locate.stage1.err',err);
return cb(err);
}
})
}
}
// 2. GPS via IP and external database service
function stage2 (cb,options,location) {
if (!https || !https.request) return cb(new Error('ENETWORK'));
if (!https.xhr && https.request) {
var e = {
hostname: ip(options.https),
port: port(options.https),
path: options.path,
method: 'GET'
}
if (Options.verbose) console.log('locate.stage2',e);
var req = https.request(e, function (res) {
res.on('data', function (d){
try {
var pos = JSON.parse(d);
if (Options.verbose) console.log('locate.stage3.res',pos);
location.gps5 = { lat: pos.location.lat, lon:pos.location.lng }
cb(location)
} catch (e) { if (Options.verbose) console.log('locate.stage2.err',e); cb(e) };
});
})
req.on('error', function (e) {
cb(e);
});
req.end();
} else {
// Browser
e={uri:'https://'+options.https+'/'+options.path,
method:"GET",
headers:{}};
if (Options.verbose) console.log('locate.stage2',e);
https.request(e,function(err,xhr,body){
if (err) {
if (Options.verbose) console.log('locate.stage2.err',err);
return cb(err);
}
try {
var pos = JSON.parse(body);
if (Options.verbose) console.log('locate.stage2.res',pos);
location.gps5 = { lat: pos.location.lat, lon:pos.location.lng }
return cb(location);
} catch (err) {
if (Options.verbose) console.log('locate.stage2.err',err);
return cb(err);
}
})
}
}
// GPS -> GEO mapping by external database service, more precise information
// Should use only gps5 (based on GPS), not gps (based in IP address)
function stage3 (cb,options,location) {
if (!https || !https.request) return cb(new Error('ENETWORK'));
// location.gps is @IP location
var gps = location.gps5 // || location.gps
if (!gps) return cb(new Error("No GPS location"));
options.path=options.path
.replace(/LAT/,gps.lat)
.replace(/LON/,gps.lon);
if (!https.xhr && https.request) {
var e = {
hostname: ip(options.https),
port: port(options.https),
path: options.path,
method: 'GET'
}
if (Options.verbose) console.log('locate.stage3',e);
var req = https.request(e, function (res) {
res.on('data', function (d){
try {
var res = JSON.parse(d);
var loc;
if (Options.verbose) console.log('locate.stage3.res',res);
if (res.address) loc=res.address;
else if (res.results && res.results[0]) loc=res.results[0].components;
location.geo5 = {
city:loc.city,
zip:loc.postcode,
street:loc.road,
number:loc.house_number,
country:loc.country
}
cb(location)
} catch (e) { if (Options.verbose) console.log('locate.stage3.err',e); cb(e) };
});
})
req.on('error', function (e) {
if (Options.verbose) console.log('locate.stage3.err',e);
cb(e);
});
req.end();
} else {
// Browser
e={uri:'https://'+options.https+'/'+options.path,
method:"GET",
headers:{}};
if (Options.verbose) console.log('locate.stage3',e);
https.request(e,function(err,xhr,body){
if (err) {
if (Options.verbose) console.log('locate.stage3.err',err);
return cb(err);
}
try {
var res = JSON.parse(body);
var loc;
if (Options.verbose) console.log('locate.stage3.res',res);
if (res.address) loc=res.address;
else if (res.results && res.results[0]) loc=res.results[0].components;
location.geo5 = {
city:loc.city,
zip:loc.postcode,
street:loc.road,
number:loc.house_number,
country:loc.country
}
return cb(location);
} catch (err) {
if (Options.verbose) console.log('locate.stage3.err',err);
return cb(err);
}
})
}
}
var todo = {
// 1. Direct ISP - IP - GPS/GEO lookup
// 1a. with proxy
stage1A: function (cb,errors,location) {
stage1(function (res) {
if (res instanceof Error) {
errors.push({url:Options.locate.primary,error:res});
todo.stage1B(cb,errors,location);
} else {
todo.stage2A(cb,errors,location);
}
},Options.locate.primary,location);
},
// 1b. w/o proxy
stage1B: function (cb,errors,location) {
stage1(function (res) {
if (res instanceof Error) {
errors.push({url:Options.locate.secondary,error:res});
todo.stage2A(cb,errors,location);
} else {
todo.stage2A(cb,errors,location);
}
},Options.locate.secondary,location);
},
// 2. Get geo position (lat,lon)
stage2A: function (cb,errors,location) {
if (Options.locate5.secondary) stage2(function (res) {
if (res instanceof Error) {
errors.push({url:Options.locate5.secondary,error:res});
todo.finalize(cb,errors,location);
} else {
todo.stage2B(cb,errors,location);
}
},Options.locate5.secondary,location);
else
todo.stage2B(cb,errors,location);
},
// 3. Get geo location (country,..)
stage2B: function (cb,errors,location) {
stage3(function (res) {
if (res instanceof Error) {
errors.push({url:Options.reverse.primary,x:1,error:res});
todo.finalize(cb,errors,location);
} else {
todo.finalize(cb,errors,location);
}
},Options.reverse.primary,location);
},
finalize : function (cb,errors,location) {
cb(location,errors);
}
}
function locate (cb,options) {
var e;
if (options) Options=Object.assign(Options,options);
if (typeof require == 'function' && !https) try {
https = require('https');
} catch (e) { /* TODO Browser */ } else if (http.xhr) https = http;
if (!http) return cb(new Error('ENOTSUPPORTED'));
todo.stage1A(cb,[],{});
return;
}
module.exports={locate:locate,options:Options};