import 'dotenv/config'; import { URL } from 'url'; import fetch from 'node-fetch'; import type { Response } from 'node-fetch'; import { createLog } from '../log'; import type { Logger } from 'winston'; import { envString } from '../env'; import type { DB, LatLng } from '../db'; export class Geocode { private log : Logger = createLog('geocode'); private baseUrl : string = 'https://geocode.maps.co/search'; private apiKey : string = envString('GEOCODE_API_KEY', null); private db : DB; constructor (db : DB) { this.db = db; } public async query (location : string) : Promise { let res : LatLng = await this.db.getLocation(location); if (res === null) { res = await this.api(location); } return res; } private toLatLng (obj : any) : LatLng { return { latitude : parseFloat(obj.lat), longitude : parseFloat(obj.lon) }; } //https://geocode.maps.co/search?q=&api_key=675738aa38619885468998kehbf6458 private async api (location : string) : Promise { const url : URL = new URL(this.baseUrl); let response : Response; let json : any; let res : LatLng = null; url.searchParams.append('q', location); this.log.info(`Querying API: ${url.href}`); url.searchParams.append('api_key', this.apiKey); await this.delay(1000); //rate limit to 1/sec try { response = await fetch(url.href); } catch (err) { this.log.error('Error getting response', err); return null; } if (response.status !== 200) { this.log.warn(`Invalid response from API [${response.status}]`); return null; } try { json = await response.json(); } catch (err) { this.log.error('Error parsing json', err); return null; } if (json.length < 1) { return null; } res = this.toLatLng(json[0]); await this.db.cacheLocation(location, res); return res; } private async delay (ms : number) { return new Promise ((resolve : Function, reject : Function) => { return setTimeout(resolve, ms); }); } private cache (location : string, latitude : number, longitude : number) { } } module.exports = { Geocode };