|
#!/usr/bin/env node |
|
|
|
const fs = require("fs"); |
|
const path = require("path"); |
|
const os = require("os"); |
|
const { execSync } = require("child_process"); |
|
|
|
// Paths |
|
const BASE_DIR = path.join(os.homedir(), ".caddyflare"); |
|
const ROUTES_FILE = path.join(BASE_DIR, "routes.json"); |
|
const CADDYFILE = path.join(BASE_DIR, "Caddyfile"); |
|
|
|
// -------------------- |
|
// Helpers |
|
// -------------------- |
|
function ensureDir() { |
|
if (!fs.existsSync(BASE_DIR)) { |
|
fs.mkdirSync(BASE_DIR, { recursive: true }); |
|
} |
|
} |
|
|
|
function loadRoutes() { |
|
if (!fs.existsSync(ROUTES_FILE)) return {}; |
|
return JSON.parse(fs.readFileSync(ROUTES_FILE, "utf-8")); |
|
} |
|
|
|
function saveRoutes(routes) { |
|
fs.writeFileSync(ROUTES_FILE, JSON.stringify(routes, null, 2)); |
|
} |
|
|
|
function generateCaddyfile(routes) { |
|
let content = `:8080 {\n`; |
|
|
|
for (const [host, port] of Object.entries(routes)) { |
|
const label = host.replace(/\./g, "-"); |
|
content += ` |
|
@${label} host ${host} |
|
reverse_proxy @${label} localhost:${port} |
|
`; |
|
} |
|
|
|
content += `}\n`; |
|
fs.writeFileSync(CADDYFILE, content.trim() + "\n"); |
|
} |
|
|
|
function reloadCaddy() { |
|
try { |
|
execSync(`caddy reload --config ${CADDYFILE}`, { |
|
stdio: "inherit", |
|
}); |
|
} catch { |
|
console.error("❌ Failed to reload Caddy"); |
|
process.exit(1); |
|
} |
|
} |
|
|
|
function openBrowser(url) { |
|
let cmd; |
|
|
|
if (process.platform === "darwin") { |
|
cmd = `open "${url}"`; |
|
} else if (process.platform === "win32") { |
|
cmd = `start "" "${url}"`; |
|
} else { |
|
cmd = `xdg-open "${url}"`; |
|
} |
|
|
|
try { |
|
execSync(cmd, { stdio: "ignore" }); |
|
} catch { |
|
console.warn("⚠️ Could not open browser automatically"); |
|
} |
|
} |
|
|
|
// -------------------- |
|
// CLI |
|
// -------------------- |
|
const args = process.argv.slice(2); |
|
const command = args[0]; |
|
|
|
if (command === "init") { |
|
ensureDir(); |
|
|
|
if (!fs.existsSync(ROUTES_FILE)) { |
|
fs.writeFileSync(ROUTES_FILE, "{}\n"); |
|
} |
|
|
|
if (!fs.existsSync(CADDYFILE)) { |
|
fs.writeFileSync(CADDYFILE, `:8080 {\n}\n`); |
|
} |
|
|
|
console.log("✅ caddyflare initialized"); |
|
console.log(`- ${ROUTES_FILE}`); |
|
console.log(`- ${CADDYFILE}`); |
|
process.exit(0); |
|
} |
|
|
|
if (command === "start") { |
|
const projectIndex = args.indexOf("--project"); |
|
const portIndex = args.indexOf("--port"); |
|
|
|
if (projectIndex === -1 || portIndex === -1) { |
|
console.error("Usage: caddyflare start --project <domain> --port <port>"); |
|
process.exit(1); |
|
} |
|
|
|
const project = args[projectIndex + 1]; |
|
const port = parseInt(args[portIndex + 1], 10); |
|
|
|
if (!project || !port) { |
|
console.error("❌ Invalid project or port"); |
|
process.exit(1); |
|
} |
|
|
|
ensureDir(); |
|
|
|
const routes = loadRoutes(); |
|
|
|
// Ensure port is unique |
|
for (const [host, usedPort] of Object.entries(routes)) { |
|
if (usedPort === port) { |
|
delete routes[host]; |
|
} |
|
} |
|
|
|
routes[project] = port; |
|
|
|
saveRoutes(routes); |
|
generateCaddyfile(routes); |
|
reloadCaddy(); |
|
|
|
const url = `https://${project}`; |
|
openBrowser(url); |
|
|
|
console.log(`✅ ${project} → localhost:${port}`); |
|
console.log(`🌐 Opened ${url}`); |
|
process.exit(0); |
|
} |
|
|
|
// -------------------- |
|
// Help |
|
// -------------------- |
|
console.log(` |
|
caddyflare commands: |
|
|
|
caddyflare init |
|
caddyflare start --project <domain> --port <port> |
|
`); |