18d307f52SEvan Baconimport dns from 'dns'; 28d307f52SEvan Baconimport { URL } from 'url'; 38d307f52SEvan Bacon 4*8c8eefe0SEvan Baconimport { fetchAsync } from '../api/rest/client'; 5*8c8eefe0SEvan Bacon 68d307f52SEvan Bacon/** Check if a server is available based on the URL. */ 78d307f52SEvan Baconexport function isUrlAvailableAsync(url: string): Promise<boolean> { 88d307f52SEvan Bacon return new Promise<boolean>((resolve) => { 98d307f52SEvan Bacon dns.lookup(url, (err) => { 108d307f52SEvan Bacon resolve(!err); 118d307f52SEvan Bacon }); 128d307f52SEvan Bacon }); 138d307f52SEvan Bacon} 148d307f52SEvan Bacon 158d307f52SEvan Bacon/** Check if a request to the given URL is `ok` (status 200). */ 168d307f52SEvan Baconexport async function isUrlOk(url: string): Promise<boolean> { 178d307f52SEvan Bacon try { 18*8c8eefe0SEvan Bacon const res = await fetchAsync(url); 198d307f52SEvan Bacon return res.ok; 208d307f52SEvan Bacon } catch { 218d307f52SEvan Bacon return false; 228d307f52SEvan Bacon } 238d307f52SEvan Bacon} 248d307f52SEvan Bacon 258d307f52SEvan Bacon/** Determine if a string is a valid URL, can optionally ensure certain protocols (like `https` or `exp`) are adhered to. */ 268d307f52SEvan Baconexport function validateUrl( 278d307f52SEvan Bacon urlString: string, 288d307f52SEvan Bacon { 298d307f52SEvan Bacon protocols, 308d307f52SEvan Bacon requireProtocol, 318d307f52SEvan Bacon }: { 328d307f52SEvan Bacon /** Set of allowed protocols for the string to adhere to. @example ['exp', 'https'] */ 338d307f52SEvan Bacon protocols?: string[]; 348d307f52SEvan Bacon /** Ensure the URL has a protocol component (prefix before `://`). */ 358d307f52SEvan Bacon requireProtocol?: boolean; 368d307f52SEvan Bacon } = {} 378d307f52SEvan Bacon) { 388d307f52SEvan Bacon try { 398d307f52SEvan Bacon const results = new URL(urlString); 408d307f52SEvan Bacon if (!results.protocol && !requireProtocol) { 418d307f52SEvan Bacon return true; 428d307f52SEvan Bacon } 438d307f52SEvan Bacon return protocols 448d307f52SEvan Bacon ? results.protocol 458d307f52SEvan Bacon ? protocols.map((x) => `${x.toLowerCase()}:`).includes(results.protocol) 468d307f52SEvan Bacon : false 478d307f52SEvan Bacon : true; 488d307f52SEvan Bacon } catch { 498d307f52SEvan Bacon return false; 508d307f52SEvan Bacon } 518d307f52SEvan Bacon} 528d307f52SEvan Bacon 538d307f52SEvan Bacon/** Remove the port from a given `host` URL string. */ 548d307f52SEvan Baconexport function stripPort(host?: string): string | null { 558d307f52SEvan Bacon return coerceUrl(host)?.hostname ?? null; 568d307f52SEvan Bacon} 578d307f52SEvan Bacon 588d307f52SEvan Baconfunction coerceUrl(urlString?: string): URL | null { 598d307f52SEvan Bacon if (!urlString) { 608d307f52SEvan Bacon return null; 618d307f52SEvan Bacon } 628d307f52SEvan Bacon try { 638d307f52SEvan Bacon return new URL('/', urlString); 6429975bfdSEvan Bacon } catch (error: any) { 658d307f52SEvan Bacon if (error.code !== 'ERR_INVALID_URL') { 668d307f52SEvan Bacon throw error; 678d307f52SEvan Bacon } 688d307f52SEvan Bacon return new URL('/', `http://${urlString}`); 698d307f52SEvan Bacon } 708d307f52SEvan Bacon} 718d307f52SEvan Bacon 728d307f52SEvan Bacon/** Strip a given extension from a URL string. */ 738d307f52SEvan Baconexport function stripExtension(url: string, extension: string): string { 748d307f52SEvan Bacon return url.replace(new RegExp(`.${extension}$`), ''); 758d307f52SEvan Bacon} 76