190 lines
6.8 KiB
JavaScript
190 lines
6.8 KiB
JavaScript
import fs from 'fs';
|
|
import path from 'path';
|
|
import {extract} from "tar";
|
|
import {Reader} from '@maxmind/geoip2-node';
|
|
|
|
const ENABLE_MAXMIND=false
|
|
|
|
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) {
|
|
if (username === '' || password === '') {
|
|
console.log('No MaxMind credentials provided. Skipping MaxMind database update.');
|
|
return
|
|
}
|
|
|
|
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;
|
|
|
|
if( ENABLE_MAXMIND ) {
|
|
|
|
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
|
|
}
|
|
}
|
|
|