import * as plugins from './cloudflare.plugins.js'; import { logger } from './cloudflare.logger.js'; export class CloudflareUtils { /** * Validates if a domain name is properly formatted * @param domainName Domain name to validate * @returns True if the domain is valid */ public static isValidDomain(domainName: string): boolean { try { const domain = new plugins.smartstring.Domain(domainName); // Check if the domain has at least a TLD and a name return domain.fullName.includes('.') && domain.zoneName.length > 0; } catch (error) { return false; } } /** * Extracts the zone name (apex domain) from a full domain * @param domainName Domain name to process * @returns Zone name (apex domain) */ public static getZoneName(domainName: string): string { try { const domain = new plugins.smartstring.Domain(domainName); return domain.zoneName; } catch (error) { logger.log('error', `Invalid domain name: ${domainName}`); throw new Error(`Invalid domain name: ${domainName}`); } } /** * Checks if a string is a valid Cloudflare API token * @param token API token to validate * @returns True if the token format is valid */ public static isValidApiToken(token: string): boolean { // Cloudflare API tokens are typically 40+ characters long and start with specific patterns return /^[A-Za-z0-9_-]{40,}$/.test(token); } /** * Validates a DNS record type * @param type DNS record type to validate * @returns True if it's a valid DNS record type */ public static isValidRecordType(type: string): boolean { const validTypes: plugins.tsclass.network.TDnsRecordType[] = [ 'A', 'AAAA', 'CNAME', 'TXT', 'SRV', 'LOC', 'MX', 'NS', 'CAA', 'CERT', 'DNSKEY', 'DS', 'NAPTR', 'SMIMEA', 'SSHFP', 'TLSA', 'URI' // Note: SPF has been removed as it's not in TDnsRecordType ]; return validTypes.includes(type as any); } /** * Formats a URL for cache purging (ensures it starts with http/https) * @param url URL to format * @returns Properly formatted URL */ public static formatUrlForPurge(url: string): string { if (!url.startsWith('http://') && !url.startsWith('https://')) { return `https://${url}`; } return url; } /** * Converts a TTL value in seconds to a human-readable string * @param ttl TTL in seconds * @returns Human-readable TTL */ public static formatTtl(ttl: number): string { if (ttl === 1) { return 'Automatic'; } else if (ttl === 120) { return '2 minutes'; } else if (ttl === 300) { return '5 minutes'; } else if (ttl === 600) { return '10 minutes'; } else if (ttl === 900) { return '15 minutes'; } else if (ttl === 1800) { return '30 minutes'; } else if (ttl === 3600) { return '1 hour'; } else if (ttl === 7200) { return '2 hours'; } else if (ttl === 18000) { return '5 hours'; } else if (ttl === 43200) { return '12 hours'; } else if (ttl === 86400) { return '1 day'; } else { return `${ttl} seconds`; } } /** * Safely handles API pagination for Cloudflare requests * @param makeRequest Function that makes the API request with page parameters * @returns Combined results from all pages */ public static async paginateResults( makeRequest: (page: number, perPage: number) => Promise<{ result: T[], result_info: { total_pages: number } }> ): Promise { const perPage = 50; // Cloudflare's maximum let page = 1; let totalPages = 1; const allResults: T[] = []; do { try { const response = await makeRequest(page, perPage); allResults.push(...response.result); totalPages = response.result_info.total_pages; page++; } catch (error) { logger.log('error', `Pagination error on page ${page}: ${error.message}`); break; } } while (page <= totalPages); return allResults; } }