@monkeyscanjump/cloudflare-dyndns
Version:
A robust TypeScript application that automatically updates Cloudflare DNS records when your public IP address changes. Perfect for maintaining consistent domain names for home servers, WireGuard VPN, self-hosted services, or any system with a dynamic IP a
160 lines • 6.25 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.IpDetectionService = void 0;
const axios_1 = __importDefault(require("axios"));
/**
* Service for detecting the current public IP address
* with multiple fallback services for reliability
*/
class IpDetectionService {
/**
* Creates a new IP detection service instance
* @param logger Logger instance for recording detection activity
* @param ipServices Array of service identifiers to use for IP detection
*/
constructor(logger, ipServices = ['ipify', 'ifconfig', 'ipinfo', 'seeip']) {
this.logger = logger;
this.ipServices = ipServices && ipServices.length > 0
? ipServices
: ['ipify', 'ifconfig', 'ipinfo', 'seeip'];
this.ipServiceEndpoints = {
'ipify': {
url: 'https://api.ipify.org?format=json',
parser: (data) => {
if (typeof data === 'string')
return data.trim();
return data.ip || '';
}
},
'ifconfig': {
url: 'https://ifconfig.me/ip',
parser: (data) => {
if (typeof data === 'string')
return data.trim();
return data.ip || '';
}
},
'ipinfo': {
url: 'https://ipinfo.io/json',
parser: (data) => {
if (typeof data === 'string')
return data.trim();
return data.ip || '';
}
},
'seeip': {
url: 'https://api.seeip.org/jsonip',
parser: (data) => {
if (typeof data === 'string')
return data.trim();
return data.ip || '';
}
},
'ipapi': {
url: 'https://ipapi.co/json',
parser: (data) => {
if (typeof data === 'string')
return data.trim();
return data.ip || '';
}
},
'myip': {
url: 'https://api.myip.com',
parser: (data) => {
if (typeof data === 'string')
return data.trim();
return data.ip || '';
}
}
};
}
/**
* Detects the current public IP address using multiple services with fallback
* @returns Promise resolving to the current public IPv4 address
* @throws Error if all configured IP detection services fail
*/
async detectIp() {
const shuffledServices = [...this.ipServices].sort(() => Math.random() - 0.5);
const errorMessages = [];
for (const service of shuffledServices) {
if (!this.ipServiceEndpoints[service]) {
this.logger.warn(`Unknown IP detection service: ${service}, skipping`);
continue;
}
try {
const endpoint = this.ipServiceEndpoints[service];
this.logger.debug(`Attempting to detect IP using ${service}`);
const response = await axios_1.default.get(endpoint.url, {
timeout: 10000,
headers: {
'User-Agent': 'CloudflareDynDNS/1.0',
'Accept': 'application/json, text/plain, */*'
}
});
if (!response.data) {
throw new Error(`Empty response from ${service}`);
}
const ip = endpoint.parser(response.data);
if (this.isValidIpv4(ip)) {
this.logger.debug(`Successfully detected IP ${ip} using ${service}`);
return ip;
}
else {
const errorMsg = `Invalid IP format received from ${service}: ${ip}`;
errorMessages.push(errorMsg);
this.logger.warn(errorMsg);
}
}
catch (error) {
const errorMsg = `Failed to detect IP using ${service}: ${error.message}`;
errorMessages.push(errorMsg);
this.logger.warn(errorMsg);
}
}
const errorDetail = errorMessages.length > 0
? `\nErrors: ${errorMessages.join('\n')}`
: '';
throw new Error(`Failed to detect public IP with all configured services.${errorDetail}`);
}
/**
* Validates if a string is a properly formatted IPv4 address
* @param ip String to validate as an IPv4 address
* @returns True if the string is a valid IPv4 address
*/
isValidIpv4(ip) {
if (!ip || typeof ip !== 'string')
return false;
const ipv4Regex = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/;
if (!ipv4Regex.test(ip))
return false;
return ip.split('.').map(Number).every(num => num >= 0 && num <= 255);
}
/**
* Attempts to get a fallback IP from an alternative source
* Used as a last resort when other methods fail
* @returns Promise resolving to an IP address or null if unavailable
*/
async getFallbackIp() {
try {
const lastResortUrl = 'https://checkip.amazonaws.com/';
const response = await axios_1.default.get(lastResortUrl, {
timeout: 5000,
headers: { 'User-Agent': 'CloudflareDynDNS/1.0' }
});
const ip = response.data.trim();
if (this.isValidIpv4(ip)) {
this.logger.debug(`Retrieved fallback IP: ${ip}`);
return ip;
}
}
catch (error) {
this.logger.debug(`Fallback IP detection failed: ${error.message}`);
}
return null;
}
}
exports.IpDetectionService = IpDetectionService;
//# sourceMappingURL=IpDetectionService.js.map