UNPKG

fvttt

Version:

FVTTT is a CLI tool for tunneling Foundry VTT application through Cloudflare.

112 lines (96 loc) 3.58 kB
import * as p from "@clack/prompts"; import { bin, install } from "cloudflared"; import fs from "node:fs"; import { execSync } from "node:child_process"; export const CLOUDFLARED_VERSION = "2026.5.2"; export const GITHUB_CLOUDFLARED_URL = "https://api.github.com/repos/cloudflare/cloudflared/releases/latest"; export const TUNNEL_TIMEOUT = 30000; /** * Fetches the latest cloudflared version from GitHub */ export async function getLatestVersion(): Promise<string | null> { try { const response = await fetch(GITHUB_CLOUDFLARED_URL); if (!response.ok) { throw new Error(`GitHub responded with status: ${response.status}`); } const data = (await response.json()) as { tag_name: string }; return data.tag_name; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); p.log.error(`Error fetching latest Cloudflared version: ${errorMessage}`); return null; } } /** * Compares two semantic version strings * @returns 1 if v1 > v2, -1 if v1 < v2, 0 if equal */ export function compareVersion(v1: string, v2: string): number { const parts1 = v1.split(".").map(Number); const parts2 = v2.split(".").map(Number); for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) { const p1 = parts1[i] || 0; const p2 = parts2[i] || 0; if (p1 > p2) return 1; if (p1 < p2) return -1; } return 0; } /** * Parses the version number from cloudflared version output */ export function parseVersion(versionOutput: string): string | null { const versionMatch = versionOutput.match(/version (\d+\.\d+\.\d+)/); return versionMatch?.[1] ?? null; } /** * Checks if cloudflared is installed and up-to-date, installs or updates if necessary */ export async function installCloudflared(): Promise<void> { const targetVersion = (await getLatestVersion()) || CLOUDFLARED_VERSION; let needsInstall = false; if (!fs.existsSync(bin)) { p.log.info("Cloudflared not found. Installing..."); needsInstall = true; } else { try { const cloudflaredVersionOutput = execSync(`"${bin}" --version`) .toString() .trim(); p.log.info(`Current ${cloudflaredVersionOutput}`); const currentVersion = parseVersion(cloudflaredVersionOutput); if (!currentVersion) { p.log.info("Unable to get current version. Will attempt to update."); needsInstall = true; } else if (compareVersion(targetVersion, currentVersion) > 0) { p.log.info( `Cloudflared version ${currentVersion} is older than ${targetVersion}. Updating...` ); needsInstall = true; } else { p.log.info(`Using Cloudflared version ${currentVersion}`); } } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); p.log.error(`Error checking Cloudflared version: ${errorMessage}`); needsInstall = true; } } if (needsInstall) { const s = p.spinner(); s.start(`Installing Cloudflared ${targetVersion}...`); try { await install(bin, targetVersion); s.stop(`Cloudflared ${targetVersion} installed successfully.`); } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); s.stop(`Failed to install Cloudflared: ${errorMessage}`); p.log.error(`Error installing Cloudflared: ${errorMessage}`); process.exit(1); } } }