renew-ip
Version:
A module for renewing 4G/LTE IP addresses on tethered Android phones from Node JS.
169 lines • 5.84 kB
JavaScript
;
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