"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.geoFilter = exports.ip2country = void 0;
const config_1 = require("./config");
const misc_1 = require("./misc");
const promises_1 = require("node:fs/promises");
const ip2location_nodejs_1 = require("ip2location-nodejs");
const lodash_1 = __importDefault(require("lodash"));
const connections_1 = require("./connections");
const ip2location = new ip2location_nodejs_1.IP2Location();
const enabled = (0, config_1.defineConfig)(misc_1.CFG.geo_enable, false);
const allow = (0, config_1.defineConfig)(misc_1.CFG.geo_allow, null);
const list = (0, config_1.defineConfig)(misc_1.CFG.geo_list, []);
const allowUnknown = (0, config_1.defineConfig)(misc_1.CFG.geo_allow_unknown, false);
enabled.sub(checkFiles);
setInterval(checkFiles, misc_1.DAY); // keep updated at run-time
// benchmark: memoize can make this 44x faster
exports.ip2country = lodash_1.default.memoize((ip) => ip2location.getCountryShortAsync(ip).then(v => v === '-' ? '' : v, () => ''));
const geoFilter = async (ctx, next) => {
    if (enabled.get() && !(0, misc_1.isLocalHost)(ctx) && !(0, misc_1.isIpLan)(ctx.ip)) {
        const { connection } = ctx.state;
        const country = connection.country ??= await (0, exports.ip2country)(ctx.ip);
        if (country)
            (0, connections_1.updateConnection)(connection, { country });
        if (!ctx.state.skipFilters && allow.get() !== null)
            if (country ? list.get().includes(country) !== allow.get() : !allowUnknown.get())
                return (0, connections_1.disconnect)(ctx, 'geo-filter');
    }
    await next();
};
exports.geoFilter = geoFilter;
function isOpen() {
    return Boolean(ip2location.getPackageVersion());
}
async function checkFiles() {
    if (!enabled.get())
        return;
    const BIN_FILE = 'IP2LOCATION-LITE-DB1.IPV6.BIN';
    const URL = `https://download.ip2location.com/lite/${BIN_FILE}.ZIP`;
    const LOCAL_FILE = 'geo_ip.bin';
    const TEMP = LOCAL_FILE + '.downloading';
    const { mtime = 0 } = await (0, misc_1.statWithTimeout)(LOCAL_FILE).catch(() => ({ mtime: 0 }));
    const name = 'geo-ip db';
    const now = Date.now();
    if (+mtime < now - 31 * misc_1.DAY) // month-old or non-existing
        try {
            const req = await (0, misc_1.httpStream)(URL);
            console.log(`Downloading ${name}`);
            await (0, misc_1.unzip)(req, path => path.toUpperCase().endsWith(BIN_FILE) && TEMP); // give a temp name
            const s = await (0, misc_1.statWithTimeout)(TEMP); // check existence
            if (s.size < 1E6)
                throw `Bad size for geo_ip: ${s.size}`;
            if (isOpen())
                ip2location.close();
            await (0, promises_1.rename)(TEMP, LOCAL_FILE);
            exports.ip2country.cache.clear?.();
            console.log(`${name} download completed`);
        }
        catch (e) {
            console.error(`Failed to download ${name}${mtime ? ", falling back on old data" : ''}:`, e?.message || String(e));
        }
    else if (isOpen())
        return;
    console.debug(`Loading ${name}`);
    ip2location.open(LOCAL_FILE); // using openAsync causes a DEP0137 error within 10 seconds
}
