`).join('');
}
function logEntry(data) {
const timestamp = new Date().toLocaleString('en-US');
const logItem = {
timestamp,
host: data.host,
status: data.valid ? 'Valid' : 'Invalid',
days_left: data.days_left,
fullData: data
};
lookupLog.unshift(logItem);
if (lookupLog.length > 50) lookupLog.pop(); // Limit log size
saveState();
renderLog();
}
function formatDate(dateStr) {
if (!dateStr) return "N/A";
try {
return new Date(dateStr).toLocaleString('en-US', {
year: 'numeric',
month: 'long',
day: 'numeric',
hour: 'numeric',
minute: 'numeric'
});
} catch (e) {
return dateStr;
}
}
function renderResults(data) {
currentResult = data;
ui.summaryHost.textContent = escapeHTML(data.host);
if (data.valid) {
ui.summaryStatus.textContent = "Valid";
ui.summaryStatus.className = "value status-valid";
} else {
ui.summaryStatus.textContent = "Invalid / Expired";
ui.summaryStatus.className = "value status-invalid";
}
ui.summaryExpires.textContent = `${data.days_left} days`;
if (data.days_left <= 0) {
ui.summaryExpires.className = "value status-invalid";
} else if (data.days_left < 14) {
ui.summaryExpires.className = "value status-warn";
} else {
ui.summaryExpires.className = "value status-valid";
}
ui.summaryIssuer.textContent = escapeHTML(data.issuer?.O || data.issuer?.CN || 'N/A');
ui.resSubject.textContent = escapeHTML(data.valid_for[0] || 'N/A');
ui.resAlgo.textContent = escapeHTML(data.signature_algorithm || 'N/A');
ui.resValidFrom.textContent = formatDate(data.valid_from);
ui.resValidTo.textContent = formatDate(data.valid_to);
ui.loader.style.display = 'none';
ui.resultsContainer.style.display = 'block';
}
function showMessage(message, isError = true) {
ui.errorMsg.textContent = message;
ui.errorMsg.style.display = 'block';
}
// --- API LOGIC ---
function sanitizeHost(host) {
return host.trim().replace(/^(https?:\/\/)/, '').replace(/\/.*$/, '');
}
async function fetchSslData(host) {
ui.loader.style.display = 'block';
ui.resultsContainer.style.display = 'none';
ui.errorMsg.style.display = 'none';
const sanitizedHost = sanitizeHost(host);
if (!sanitizedHost) {
showMessage("Please enter a valid host name.");
ui.loader.style.display = 'none';
return;
}
try {
const response = await fetch(`https://api.ssl.lol/api/check?host=${encodeURIComponent(sanitizedHost)}`);
if (!response.ok) {
throw new Error(`API returned status ${response.status}`);
}
const data = await response.json();
if (data.status === 'error' || !data.valid_to) {
throw new Error(data.message || 'Invalid or unreachable host.');
}
renderResults(data);
logEntry(data);
} catch (error) {
console.error("SSL Fetch Error:", error);
showMessage(error.message);
} finally {
ui.loader.style.display = 'none';
}
}
// --- PDF LOGIC ---
function downloadPDF(type) {
const { jsPDF } = window.jspdf;
if (!jsPDF || !window.jspdf.autoTable) { alert("PDF library not loaded!"); return; }
const doc = new jsPDF();
if (type === 'current') {
if (!currentResult.host) {
alert("Please run a lookup before downloading.");
return;
}
doc.setFontSize(18);
doc.text(`SSL Certificate Report: ${currentResult.host}`, 14, 22);
doc.autoTable({
startY: 30,
theme: 'grid',
body: [
['Host', currentResult.host],
['Status', currentResult.valid ? 'Valid' : 'Invalid/Expired'],
['Expires In', `${currentResult.days_left} days`],
['Subject', currentResult.valid_for[0] || 'N/A'],
['Issuer', currentResult.issuer?.O || currentResult.issuer?.CN || 'N/A'],
['Valid From', formatDate(currentResult.valid_from)],
['Valid To', formatDate(currentResult.valid_to)],
['Algorithm', currentResult.signature_algorithm],
]
});
doc.save(`SSL_Report_${currentResult.host}.pdf`);
} else if (type === 'log') {
if (lookupLog.length === 0) {
alert("Log is empty. Please perform a lookup first.");
return;
}
doc.setFontSize(18);
doc.text("SSL Lookup Log", 14, 22);
doc.autoTable({
startY: 30,
head: [['Timestamp', 'Host', 'Status', 'Days Left']],
body: lookupLog.map(log => [log.timestamp, log.host, log.status, log.days_left]),
theme: 'striped',
headStyles: { fillColor: [55, 48, 163] } // var(--primary-color)
});
doc.save("SSL_Lookup_Log.pdf");
}
}
// --- EVENT LISTENERS ---
ui.form.addEventListener('submit', e => {
e.preventDefault();
const host = ui.input.value;
fetchSslData(host);
});
ui.downloadReportBtn.addEventListener('click', () => downloadPDF('current'));
ui.downloadLogBtn.addEventListener('click', () => downloadPDF('log'));
ui.clearLogBtn.addEventListener('click', () => {
lookupLog = [];
saveState();
renderLog();
});
// --- TABBING LOGIC ---
function switchTab(targetTabId) {
ui.tabContents.forEach(c => c.classList.remove('active'));
ui.tabButtons.forEach(b => b.classList.remove('active'));
document.getElementById(`ssl-tab-${targetTabId}`).classList.add('active');
document.querySelector(`.ssl-tab-button[data-tab="${targetTabId}"]`).classList.add('active');
}
function navigateTabs(dir) {
const tabs = Array.from(ui.tabButtons);
const active = tabs.find(t => t.classList.contains('active'));
let index = tabs.indexOf(active) + dir;
if (index < 0) index = 0; if (index >= tabs.length) index = tabs.length - 1;
switchTab(tabs[index].dataset.tab);
}
ui.tabButtons.forEach(button => button.addEventListener('click', () => switchTab(button.dataset.tab)));
ui.nextTabBtn.addEventListener('click', () => navigateTabs(1));
ui.prevTabBtn.addEventListener('click', () => navigateTabs(-1));
// --- UTILITY ---
function escapeHTML(str) {
if (typeof str !== 'string') return '';
const p = document.createElement('p');
p.textContent = str;
return p.innerHTML;
}
// --- INITIALIZATION ---
loadState();
renderLog();
});