kopia lustrzana https://github.com/FacilMap/facilmap
98 wiersze
2.7 KiB
TypeScript
98 wiersze
2.7 KiB
TypeScript
import { distanceToDegreesLat, distanceToDegreesLon } from "./utils/geo";
|
|
import md5 from "md5-file";
|
|
import cron from "node-cron";
|
|
import maxmind, { Reader, Response } from "maxmind";
|
|
import fs from "fs";
|
|
import https from "https";
|
|
import zlib from "zlib";
|
|
import config from "./config";
|
|
import { IncomingMessage } from "http";
|
|
import { Bbox } from "facilmap-types";
|
|
|
|
const url = "https://updates.maxmind.com/geoip/databases/GeoLite2-City/update?db_md5=";
|
|
const fname = `${__dirname}/../cache/GeoLite2-City.mmdb`;
|
|
const tmpfname = fname + ".tmp";
|
|
|
|
let currentMd5: string | null = null;
|
|
let db: Reader<Response> | null = null;
|
|
|
|
if(config.maxmindUserId && config.maxmindLicenseKey) {
|
|
cron.schedule("0 3 * * *", download);
|
|
|
|
load().catch((err) => {
|
|
console.trace("Error loading maxmind database", err.stack || err);
|
|
});
|
|
download().catch((err) => {
|
|
console.trace("Error downloading maxmind database", err.stack || err);
|
|
});
|
|
}
|
|
|
|
async function load() {
|
|
if(await fs.promises.access(fname).then(() => true).catch(() => false))
|
|
db = await maxmind.open(fname);
|
|
else
|
|
db = null;
|
|
|
|
|
|
}
|
|
|
|
async function download() {
|
|
console.log("Downloading maxmind database");
|
|
|
|
if(!currentMd5) {
|
|
if(await fs.promises.access(fname).then(() => true).catch(() => false))
|
|
currentMd5 = await md5(fname);
|
|
}
|
|
|
|
const res = await new Promise<IncomingMessage>((resolve, reject) => {
|
|
https.get(url + (currentMd5 || ""), {
|
|
headers: {
|
|
Authorization: `Basic ${new Buffer(config.maxmindUserId + ':' + config.maxmindLicenseKey).toString('base64')}`
|
|
}
|
|
}, resolve).on("error", reject);
|
|
});
|
|
|
|
if(res.statusCode == 304) {
|
|
console.log("Maxmind database is up to date, no update needed.");
|
|
return;
|
|
} else if(res.statusCode != 200)
|
|
throw new Error(`Unexpected status code ${res.statusCode} when downloading maxmind database.`);
|
|
|
|
const gunzip = zlib.createGunzip();
|
|
res.pipe(gunzip);
|
|
|
|
const file = fs.createWriteStream(tmpfname);
|
|
gunzip.pipe(file);
|
|
|
|
await new Promise((resolve, reject) => {
|
|
file.on("finish", resolve);
|
|
file.on("error", reject);
|
|
});
|
|
|
|
await fs.promises.rename(tmpfname, fname);
|
|
currentMd5 = await md5(fname);
|
|
|
|
await load();
|
|
|
|
console.log("Maxmind database downloaded");
|
|
}
|
|
|
|
export async function geoipLookup(ip: string): Promise<Bbox | undefined> {
|
|
if(!db)
|
|
return undefined;
|
|
|
|
const ret = db.get(ip);
|
|
|
|
if(ret && 'location' in ret && ret.location) {
|
|
const distLat = distanceToDegreesLat(ret.location.accuracy_radius);
|
|
const distLon = distanceToDegreesLon(ret.location.accuracy_radius, ret.location.latitude);
|
|
|
|
return {
|
|
top: ret.location.latitude + distLat,
|
|
right: ret.location.longitude + distLon,
|
|
bottom: ret.location.latitude - distLat,
|
|
left: ret.location.longitude - distLon
|
|
};
|
|
}
|
|
}
|