UNPKG

renew-ip

Version:

A module for renewing 4G/LTE IP addresses on tethered Android phones from Node JS.

169 lines 5.84 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Router = void 0; const tslib_1 = require("tslib"); const events_1 = tslib_1.__importDefault(require("events")); const api_1 = require("./api"); const errors_1 = require("./errors"); const schemas_1 = require("./schemas"); const utils_1 = require("./utils"); /** * Renewable 4G Router * * @class * @classdesc Renews an IP address on a device connected with Automate. * @extends {EventEmitter} * @implements {RenewableRouter} */ class Router extends events_1.default { /** * Constructor * Accepts an optional configuration if not using env file. * * @param {PartialRouterConfig} config */ constructor(config) { super(); utils_1.debug(`Constructing router.`); // Parse config. this._config = schemas_1.RouterConfigSchema.parse(config); // Init history. this._history = new Set(); this._cache = new Set(); utils_1.debug(`Router constructed.`); } /** * Readonly accessor for current ip address. * * @return {string | undefined} */ get ip() { return utils_1.getLastIp(this._history); } /** * Readonly accessor to show current renewal attempts. * * @return {number} */ get attempts() { return this._cache.size; } /** * Allow users to observe last cached public ip. * * @return {string | undefined} */ get lastCachedIp() { return utils_1.getLastIp(this._cache); } /** * Public readonly accessor for history as array. * * @return {IpHistory[]} */ get history() { return [...(this._history.values() || [])]; } /** * Inform user when a new ip has been obtained or drop if dirty address. * * @return {boolean} */ validateIp(data) { // Drop if no response. if (!data) { return false; } // Loop over threat detections. for (const [key, value] of Object.entries(data.threat || {})) { if (value) { utils_1.debug(`IP flagged as "${key}"! Rejecting.`); return false; } } // Check if ip already used. return data.ip !== this.lastCachedIp; } /** * Return the module to a clean state. * * @return {Promise<void>} */ async init() { utils_1.debug(`Initialising router.`); const record = await this.lookup(); this._history.add(record); this._cache.add(record); this.emit("init"); utils_1.debug(`Initialisation complete.`); } /** * Connects to the IPData API and checks the current public ip. * * @return {Promise<IpHistory>} */ async lookup() { utils_1.debug(`Attempting lookup of public IP address.`); const { key, fields } = this._config.ipdata; return { ...(await api_1.getIpData({ key, fields })), ...{ timestamp: Date.now() } }; } /** * Primary entry point that triggers the renewal. * * @return {Promise<boolean>} */ async renew() { // Reset the module to a known state. utils_1.debug(`Starting IP renewal cycle.`); await this.init(); // Destructure config items. const { maxAttempts, delay, secret, to, payload, device, priority } = this._config; // Set initial ip to invalid state. let ip = undefined; let hasToggled = false; // Attempt renewals until a new IP is obtained, or hits max attempts. utils_1.debug(`Attempting renewal with a maximum of ${maxAttempts} attempts.`); while (!this.validateIp(ip) && this.attempts < maxAttempts) { if (!hasToggled) { // Send payload to Automate to trigger the flow on the device. utils_1.debug(`Attempting to toggle flight mode remotely.`); await api_1.toggleFlightMode({ secret, to, payload, device, priority }); hasToggled = true; } // Only attempt an ip check if device has connectivity. if (await utils_1.hasConnection()) { utils_1.debug(`Interface has connectivity!`); // Check host's public ip address. ip = schemas_1.IpHistorySchema.parse(await this.lookup()); utils_1.debug(`Reflected IP context: `, ip); // Add the current public ip to cache. this._cache.add(ip); // If new IP is different to last ip, return a success response. if (this.validateIp(ip)) { // Add to the history set to register new public ip. this._history.add(ip); this.emit("success", ip); return true; } else { // Received invalid IP, try for a new IP. this._cache.clear(); hasToggled = false; } } // Wait for a specified delay between connection checks. utils_1.debug(`Connection not online, waiting ${delay} ms.`); await utils_1.waitForTimeout(delay); } // Renewal process failed, return a negative response. this._cache.clear(); utils_1.debug(`Exceeded maximum attempts of ${maxAttempts}.`); this.emit("error", new errors_1.RenewalFailedError(maxAttempts)); return false; } } exports.Router = Router; //# sourceMappingURL=router.js.map