MaxMind IP database & region approvals
This commit is contained in:
180
maxmind.js
Normal file
180
maxmind.js
Normal file
@@ -0,0 +1,180 @@
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import {extract} from "tar";
|
||||
import {Reader} from '@maxmind/geoip2-node';
|
||||
|
||||
let ipdb = null
|
||||
function setDbFile(file) {
|
||||
const dbBuffer = fs.readFileSync(file);
|
||||
ipdb = Reader.openBuffer(dbBuffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Downloads and extracts a .tar.gz file from a given URL.
|
||||
* @param {string} url - The URL to download the file from.
|
||||
* @param {string} outputDir - The directory to move the final .mmdb file to.
|
||||
* @param {string} tempDir - The directory to use for temporary files.
|
||||
* @param {string} username - The username for HTTP basic auth.
|
||||
* @param {string} password - The password for HTTP basic auth.
|
||||
*/
|
||||
async function downloadAndExtractMaxmindData(url, outputDir, tempDir, username, password) {
|
||||
console.log('Downloading MaxMind database...');
|
||||
const tempFilePath = path.join(tempDir, `temp_${Date.now()}_${Math.random().toString(36).slice(2, 10)}.tar.gz`);
|
||||
const tempExtractDir = path.join(tempDir, `temp_extract_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`);
|
||||
|
||||
try {
|
||||
// Ensure the output directory exists
|
||||
if (!fs.existsSync(outputDir)) {
|
||||
fs.mkdirSync(outputDir, {recursive: true});
|
||||
}
|
||||
|
||||
// Create a temporary extract directory
|
||||
if (!fs.existsSync(tempExtractDir)) {
|
||||
fs.mkdirSync(tempExtractDir, {recursive: true});
|
||||
}
|
||||
|
||||
// Download the file with HTTP basic authentication and save it as a temporary tar.gz file
|
||||
const response = await fetch(url, {
|
||||
headers: {
|
||||
Authorization: 'Basic ' + Buffer.from(`${username}:${password}`).toString('base64')
|
||||
}
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to download file: ${response.statusText}`);
|
||||
}
|
||||
|
||||
const fileStream = fs.createWriteStream(tempFilePath);
|
||||
const reader = response.body.getReader();
|
||||
await new Promise((resolve, reject) => {
|
||||
function processChunk({done, value}) {
|
||||
if (done) {
|
||||
fileStream.end();
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
fileStream.write(value, () => reader.read().then(processChunk).catch(reject));
|
||||
}
|
||||
|
||||
reader.read().then(processChunk).catch(reject);
|
||||
});
|
||||
|
||||
// Extract the tar.gz file into the temporary extract directory
|
||||
await extract({
|
||||
file: tempFilePath,
|
||||
cwd: tempExtractDir
|
||||
});
|
||||
|
||||
// Find the .mmdb file in the temporary extract directory
|
||||
const mmdbFile = fs.readdirSync(tempExtractDir, {withFileTypes: true})
|
||||
.map(dirent => {
|
||||
const fullPath = path.join(tempExtractDir, dirent.name);
|
||||
if (dirent.isDirectory()) {
|
||||
const subFiles = fs.readdirSync(fullPath).map(subFile => path.join(fullPath, subFile));
|
||||
return subFiles.find(file => file.endsWith('.mmdb')) || null;
|
||||
}
|
||||
return dirent.name.endsWith('.mmdb') ? fullPath : null;
|
||||
})
|
||||
.filter(Boolean)[0];
|
||||
|
||||
if (!mmdbFile) {
|
||||
throw new Error('No .mmdb file found in the extracted contents.');
|
||||
}
|
||||
|
||||
// Move the .mmdb file to the output directory
|
||||
const dest = path.join(outputDir, path.basename(mmdbFile));
|
||||
fs.copyFileSync(
|
||||
mmdbFile,
|
||||
dest
|
||||
);
|
||||
console.log(`MaxMind database downloaded to ${dest}`);
|
||||
return dest
|
||||
} finally {
|
||||
// Clean up the temporary tar.gz file and temporary extract directory
|
||||
if (fs.existsSync(tempFilePath)) {
|
||||
fs.unlinkSync(tempFilePath);
|
||||
}
|
||||
if (fs.existsSync(tempExtractDir)) {
|
||||
fs.rmSync(tempExtractDir, {recursive: true, force: true});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks for up-to-date MaxMind database files in the specified output directory. If none of the files
|
||||
* meet the update criteria, the function downloads and extracts updated MaxMind data from the provided source.
|
||||
*
|
||||
* @param {string} outputDir - Directory where MaxMind database files are stored.
|
||||
* @param {string} tempDir - Temporary directory used during the download and extraction process.
|
||||
* @param {string} url - URL to download the MaxMind data.
|
||||
* @param {string} username - Username for authentication to access the MaxMind download service.
|
||||
* @param {string} password - Password for authentication to access the MaxMind download service.
|
||||
* @return {Promise<void>} Resolves when the data check and optional update process is completed.
|
||||
*/
|
||||
async function checkAndUpdateMaxmindData(outputDir, tempDir, url, username, password) {
|
||||
console.log('Checking for MaxMind database updates.');
|
||||
|
||||
// Ensure the output directory exists
|
||||
if (!fs.existsSync(outputDir)) {
|
||||
fs.mkdirSync(outputDir, {recursive: true});
|
||||
}
|
||||
|
||||
const mmdbFiles = fs.readdirSync(outputDir).filter(file => file.endsWith('.mmdb'));
|
||||
let shouldDownload = true;
|
||||
|
||||
if (mmdbFiles.length) {
|
||||
for (const mmdbFile of mmdbFiles) {
|
||||
const filePath = path.join(outputDir, mmdbFile);
|
||||
const stats = fs.statSync(filePath);
|
||||
const modifiedDate = new Date(stats.mtime);
|
||||
const fourDaysAgo = new Date(Date.now() - 4 * 24 * 60 * 60 * 1000);
|
||||
|
||||
if (modifiedDate > fourDaysAgo) {
|
||||
shouldDownload = false;
|
||||
console.log(`MaxMind database '${mmdbFile}' is recent.`);
|
||||
setDbFile(filePath)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldDownload) {
|
||||
setDbFile(await downloadAndExtractMaxmindData(url, outputDir, tempDir, username, password));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const url = 'https://download.maxmind.com/geoip/databases/GeoIP2-Country/download?suffix=tar.gz';
|
||||
const outputDirectory = './maxmind';
|
||||
const username = '1102431';
|
||||
const password = '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';
|
||||
// const username = process.env.MAXMIND_ACCOUNT_NUMBER;
|
||||
// const password = process.env.MAXMIND_LICENSE_KEY;
|
||||
|
||||
await checkAndUpdateMaxmindData(outputDirectory, '/tmp', url, username, password);
|
||||
|
||||
|
||||
setInterval(async () => {
|
||||
try {
|
||||
await checkAndUpdateMaxmindData(outputDirectory, '/tmp', url, username, password);
|
||||
} catch (error) {
|
||||
console.error('Error during MaxMind database update:', error);
|
||||
}
|
||||
}, 24 * 60 * 60 * 1000 + 1000); // 1 day + 1 second
|
||||
|
||||
|
||||
export function countryForIP(ipAddress) {
|
||||
if (!ipdb) return null
|
||||
try {
|
||||
const code = ipdb.country(ipAddress).country.isoCode;
|
||||
console.log(ipAddress, code)
|
||||
return code;
|
||||
}
|
||||
catch (e) {
|
||||
console.warn(`IP lookup failed for ${ipAddress}: ${e.message}`)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
const ip = '64.178.217.35'
|
||||
console.log(`ip: ${ip}`, countryForIP(ip))
|
||||
Reference in New Issue
Block a user