126 lines
3.4 KiB
JavaScript
Executable File
126 lines
3.4 KiB
JavaScript
Executable File
// sslCron.mjs
|
|
|
|
import cron from "node-cron";
|
|
import axios from "axios";
|
|
|
|
// Base URL of your SSL manager Express API
|
|
// e.g. the server where you defined /dns/domains and /ssl/refresh
|
|
const API_BASE =
|
|
process.env.SSL_MANAGER_URL || "https://api.hestiacp.metatronhost.com";
|
|
|
|
/**
|
|
* Parse "notAfter" like: "Mar 11 19:08:24 2026 GMT"
|
|
*/
|
|
function parseNotAfter(notAfterStr) {
|
|
if (!notAfterStr) return null;
|
|
const d = new Date(notAfterStr); // JS can parse this format directly
|
|
if (isNaN(d.getTime())) return null;
|
|
return d;
|
|
}
|
|
|
|
/**
|
|
* Return days left until SSL expiry from notAfter.
|
|
* If invalid → return large negative number.
|
|
*/
|
|
function daysLeftFromSsl(notAfterStr) {
|
|
const expiry = parseNotAfter(notAfterStr);
|
|
if (!expiry) return -9999;
|
|
|
|
const now = new Date();
|
|
const diffMs = expiry.getTime() - now.getTime();
|
|
const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24));
|
|
return diffDays;
|
|
}
|
|
|
|
/**
|
|
* Decide if SSL is expired based on notAfter
|
|
*/
|
|
function isSslExpired(notAfterStr) {
|
|
const expiry = parseNotAfter(notAfterStr);
|
|
if (!expiry) return true; // if we can't parse it, treat as expired
|
|
const now = new Date();
|
|
return now > expiry;
|
|
}
|
|
|
|
/**
|
|
* One cycle:
|
|
* - Fetch domain list (/dns/domains)
|
|
* - For each domain, look at ssl.notAfter
|
|
* - If expired -> (for now) just console.log the renew call
|
|
*/
|
|
async function checkAndRenewOnce() {
|
|
try {
|
|
console.log("🔍 [SSL CRON] Fetching domains...");
|
|
const res = await axios.get(`${API_BASE}/dns/domains`);
|
|
|
|
if (!res.data?.success) {
|
|
console.error("❌ [SSL CRON] /dns/domains failed:", res.data);
|
|
return;
|
|
}
|
|
|
|
const rawParsed = res.data.rawParsed || [];
|
|
console.log(`📦 [SSL CRON] Found ${rawParsed.length} DNS entries`);
|
|
|
|
for (const row of rawParsed) {
|
|
const { domain, ssl } = row;
|
|
|
|
const notAfter = ssl?.notAfter || null;
|
|
const left = daysLeftFromSsl(notAfter);
|
|
const expired = isSslExpired(notAfter);
|
|
|
|
console.log(
|
|
`🌐 Domain: ${domain} | notAfter: ${notAfter} | daysLeft: ${left} | expired: ${expired}`
|
|
);
|
|
|
|
if (!expired) {
|
|
continue; // SSL still valid, skip
|
|
}
|
|
|
|
// 🔁 For now: only log what we *would* do
|
|
console.log(
|
|
`🔁 [SSL CRON] Would renew SSL for ${domain} via POST ${API_BASE}/ssl/refresh`
|
|
);
|
|
|
|
// When you're ready to actually renew, uncomment this block:
|
|
/*
|
|
try {
|
|
console.log(`🔁 [SSL CRON] Renewing SSL for ${domain}...`);
|
|
const refreshRes = await axios.post(`${API_BASE}/ssl/refresh`, {
|
|
domain,
|
|
});
|
|
|
|
if (refreshRes.data?.success) {
|
|
console.log(`✅ [SSL CRON] SSL renewed for ${domain}`);
|
|
} else {
|
|
console.error(
|
|
`⚠️ [SSL CRON] Failed to renew ${domain}:`,
|
|
refreshRes.data
|
|
);
|
|
}
|
|
} catch (err) {
|
|
console.error(
|
|
`❌ [SSL CRON] Error renewing ${domain}:`,
|
|
err.response?.data || err.message
|
|
);
|
|
}
|
|
*/
|
|
}
|
|
|
|
console.log("🎯 [SSL CRON] Cycle completed.");
|
|
} catch (err) {
|
|
console.error(
|
|
"❌ [SSL CRON] Error fetching domains:",
|
|
err.response?.data || err.message
|
|
);
|
|
}
|
|
}
|
|
|
|
// 🔁 Schedule to run once per day at 03:00 server time
|
|
cron.schedule("0 3 * * *", () => {
|
|
console.log("⏰ [SSL CRON] Daily job started...");
|
|
checkAndRenewOnce();
|
|
});
|
|
|
|
// Optional: run immediately when starting the script
|
|
checkAndRenewOnce();
|