// server.mjs (or server.js with "type": "module") import express from "express"; import axios from "axios"; import https from "https"; // 👉 Environment / Secrets const API = process.env.HESTIA_API_HASH || "vvQjauyYQ2YDY2Au-_DC4QO5MUPFiHNn"; const SERVER = process.env.HESTIA_SERVER_URL || "https://host.metatronhost.com:8083/api/"; // 👉 FIXED Hestia username const HESTIA_USER = "user"; // Ignore self-signed SSL const httpsAgent = new https.Agent({ rejectUnauthorized: false, }); // =============================== // 🔧 H E S T I A H E L P E R S // =============================== async function hestia(cmd, args = {}) { const form = new URLSearchParams({ hash: API, cmd, ...args, }); const res = await axios.post(SERVER, form, { httpsAgent }); return res.data; } // Fetch raw DNS domain table async function getRawDomainList() { const form = new URLSearchParams({ hash: API, cmd: "v-list-dns-domains", arg1: HESTIA_USER, }); const res = await axios.post(SERVER, form, { httpsAgent }); return res.data; } // Parse domains from raw table function parseDomainsFromTable(text) { const lines = text.split("\n"); const domains = []; for (let line of lines) { line = line.trim(); if (!line.includes(".")) continue; if (line.startsWith("DOMAIN")) continue; if (line.startsWith("------")) continue; const parts = line.split(/\s+/); const domain = parts[0]; if (domain && domain.includes(".")) { domains.push(domain); } } return domains; } // Remove SSL certificate async function removeSsl(domain) { return hestia("v-delete-web-domain-ssl", { arg1: HESTIA_USER, arg2: domain, }); } // Add Let's Encrypt SSL async function addSsl(domain) { return hestia("v-add-letsencrypt-domain", { arg1: HESTIA_USER, arg2: domain, }); } // Remove + Add (renew) async function refreshSsl(domain) { const removed = await removeSsl(domain); const added = await addSsl(domain); return { removed, added }; } function parseRawTable(text) { const lines = text.split("\n").map(l => l.trim()).filter(Boolean); const dataRows = []; // Skip first 2 header lines for (let i = 2; i < lines.length; i++) { const line = lines[i]; const parts = line.split(/\s+/); // Expect at least 8 columns if (parts.length < 8) continue; const [ domain, ip, tpl, ttl, dnssec, rec, spnd, date ] = parts; dataRows.push({ domain, ip, tpl, ttl: Number(ttl), dnssec, rec: Number(rec), spnd, date }); } return dataRows; } // =============================== // 🚀 E X P R E S S A P I // =============================== const app = express(); app.use(express.json()); // Health app.get("/", (req, res) => { res.json({ running: true, user: HESTIA_USER }); }); // Get all DNS domains app.get("/dns/domains", async (req, res) => { try { const raw = await getRawDomainList(); const domains = parseDomainsFromTable(raw); const rawParsed = parseRawTable(raw); res.json({ success: true, domains, rawParsed }); } catch (err) { res.status(500).json({ success: false, error: err.response?.data || err.message }); } }); // Remove SSL app.post("/ssl/remove", async (req, res) => { const { domain } = req.body; if (!domain) { return res.status(400).json({ success: false, error: "domain is required" }); } try { const result = await removeSsl(domain); res.json({ success: true, domain, result }); } catch (err) { res.status(500).json({ success: false, domain, error: err.response?.data || err.message, }); } }); // Add SSL app.post("/ssl/add", async (req, res) => { const { domain } = req.body; if (!domain) { return res.status(400).json({ success: false, error: "domain is required" }); } try { const result = await addSsl(domain); res.json({ success: true, domain, result }); } catch (err) { res.status(500).json({ success: false, domain, error: err.response?.data || err.message, }); } }); // Refresh SSL app.post("/ssl/refresh", async (req, res) => { const { domain } = req.body; if (!domain) { return res.status(400).json({ success: false, error: "domain is required" }); } try { const result = await refreshSsl(domain); res.json({ success: true, domain, result }); } catch (err) { res.status(500).json({ success: false, domain, error: err.response?.data || err.message, }); } }); // Refresh SSL for ALL domains app.post("/ssl/refresh-all", async (req, res) => { try { const raw = await getRawDomainList(); const domains = parseDomainsFromTable(raw); const results = []; for (const domain of domains) { try { const result = await refreshSsl(domain); results.push({ domain, success: true, result }); } catch (e) { results.push({ domain, success: false, error: e.response?.data || e.message, }); } } res.json({ success: true, user: HESTIA_USER, results, }); } catch (err) { res.status(500).json({ success: false, error: err.response?.data || err.message, }); } }); // Start server const PORT = process.env.PORT || 3015; app.listen(PORT, () => { console.log(`🔥 SSL Manager API running at port ${PORT}`); });