2023-01-13 22:28:38 +01:00
const rateLimit = require ( 'express-rate-limit' ) ;
const log = require ( '../../log' )
2023-01-19 01:29:24 +01:00
const NOMINATIM _URL = 'https://nominatim.openstreetmap.org/search'
const PHOTON _URL = 'https://photon.komoot.io/api/'
const axios = require ( 'axios' )
const { version } = require ( '../../../package.json' )
const cache = require ( 'memory-cache' ) ;
let d = 0 // departure time
let h = 0 // hit geocoding provider time (aka Latency)
2023-01-13 22:28:38 +01:00
const geolocationController = {
2023-01-19 01:29:24 +01:00
/ * *
* TODO : replace / merge with a general 'instance rate-limiter' or 'instance api-related rate-limiter' when this will be defined
* /
instanceApiRateLimiter : rateLimit ( {
2023-01-13 22:28:38 +01:00
windowMs : 15 * 60 * 1000 , // 15 minutes
max : 100 , // Limit each IP to 100 requests per `window` (here, per 15 minutes)
standardHeaders : true , // Return rate limit info in the `RateLimit-*` headers
legacyHeaders : false , // Disable the `X-RateLimit-*` headers
} ) ,
/ * *
* Limit api usage
* From https : //operations.osmfoundation.org/policies/nominatim/
* [ Requirements ] No heavy uses ( an absolute maximum of 1 request per second ) .
2023-01-19 01:29:24 +01:00
* [ Websites and Apps ]
* - Note that the usage limits above apply per website / application : the sum of traffic by all your users should not exceed the limits .
* - If at all possible , set up a proxy and also enable caching of requests .
2023-01-13 22:28:38 +01:00
* /
2023-01-19 01:29:24 +01:00
providerRateLimit ( req , res , next ) {
let a = Date . now ( ) ; // arrival time
let dprev = d
d = dprev + 1000 + h
console . log ( 'a: ' + a )
console . log ( 'dprev: ' + dprev )
console . log ( 'd: ' + d )
// if the same request was already cached skip the delay mechanism
if ( cache . get ( req . params . place _details ) ) {
if ( a < d ) {
log . warn ( 'More than 1 request per second to geolocation api. This from ' + req . ip + ' . The response data is served from memory-cache' )
2023-01-14 14:53:51 +01:00
}
2023-01-19 01:29:24 +01:00
// reset departure time because there is no need to ask provider
d = dprev
return next ( )
}
if ( d === 0 || a > d ) {
// no-queue or old-queue
console . log ( 'No queue or Old queue' )
// arrival time + 10ms estimated computing time
d = a + 10
next ( )
2023-01-14 14:53:51 +01:00
} else {
2023-01-19 01:29:24 +01:00
// fresh queue
console . log ( 'Fresh queue' )
let wait = d - a
console . log ( 'Waiting ' + wait )
log . warn ( 'More than 1 request per second to geolocation api. This from ' + req . ip + ' . Applying ToS padding before asking to provider. The response data is now cached.' )
setTimeout ( ( ) => {
next ( )
} , wait )
2023-01-13 22:28:38 +01:00
}
2023-01-19 01:29:24 +01:00
} ,
async _nominatim ( req , res ) {
const details = req . params . place _details
const countrycodes = res . locals . settings . geocoding _countrycodes || [ ]
const geocoding _provider = res . locals . settings . geocoding _provider || NOMINATIM _URL
// ?limit=3&format=json&namedetails=1&addressdetails=1&q=
const ret = await axios . get ( ` ${ geocoding _provider } ` , {
params : {
countrycodes : countrycodes . join ( ',' ) ,
q : details ,
limit : 3 ,
format : 'json' ,
addressdetails : 1 ,
namedetails : 1 ,
} ,
headers : { 'User-Agent' : ` gancio ${ version } ` }
} )
return res . json ( ret . data )
} ,
async _photon ( req , res ) {
const details = req . params . place _details
const geocoding _provider = res . locals . settings . geocoding _provider || PHOTON _URL
if ( cache . get ( details ) ) {
console . log ( 'Retrieving data from cache' )
const ret = {
data : await cache . get ( details )
}
return res . json ( ret . data )
} else {
let RTTstart = Date . now ( )
console . log ( 'Asking Provider: ' + RTTstart )
const ret = await axios . get ( ` ${ geocoding _provider } ` , {
params : {
q : details ,
limit : 3 ,
} ,
headers : { 'User-Agent' : ` gancio ${ version } ` }
} )
if ( ret ) {
let RTTend = Date . now ( )
console . log ( 'Response arrived: ' + RTTend )
// Save the hit time (aka Latency)
h = ( RTTend - RTTstart ) / 2
console . log ( 'Saving latency h: ' + h )
}
console . log ( 'Caching results' )
cache . put ( details , ret . data ) ;
return res . json ( ret . data )
}
} ,
2023-01-13 22:28:38 +01:00
}
module . exports = geolocationController