// certificates.js - OpenVPN Certificate Statistics Logic // Access globals defined in PHP const { apiUrl, refreshTime } = window.AppConfig; let refreshInterval; let allCertificatesData = []; let hideExpired = false; function getStatusBadge(daysRemaining) { if (!daysRemaining || daysRemaining === 'N/A') { return 'Unknown'; } if (daysRemaining.includes('Expired')) { return 'Expired'; } else if (daysRemaining.includes('days')) { const days = parseInt(daysRemaining); if (days <= 30) { return 'Expiring Soon'; } else { return 'Valid'; } } return 'Unknown'; } function categorizeCertificates(certificates) { const active = []; const expired = []; certificates.forEach(cert => { if (!cert.days_remaining || cert.days_remaining === 'N/A') { // If days_remaining is not available, check not_after date if (cert.not_after && cert.not_after !== 'N/A') { try { const expDate = new Date(cert.not_after); const now = new Date(); if (expDate < now) { expired.push(cert); } else { active.push(cert); } } catch (e) { // If we can't parse the date, consider it active active.push(cert); } } else { // If no expiration info, consider it active active.push(cert); } } else if (cert.days_remaining.includes('Expired')) { expired.push(cert); } else { active.push(cert); } }); // Sort active certificates by days remaining (ascending) active.sort((a, b) => { try { const aDays = a.days_remaining && a.days_remaining !== 'N/A' ? parseInt(a.days_remaining) : 9999; const bDays = b.days_remaining && b.days_remaining !== 'N/A' ? parseInt(b.days_remaining) : 9999; return aDays - bDays; } catch (e) { return 0; } }); // Sort expired certificates by days expired (descending) expired.sort((a, b) => { try { const aMatch = a.days_remaining ? a.days_remaining.match(/\d+/) : [0]; const bMatch = b.days_remaining ? b.days_remaining.match(/\d+/) : [0]; const aDays = parseInt(aMatch[0]); const bDays = parseInt(bMatch[0]); return bDays - aDays; } catch (e) { return 0; } }); return { active, expired }; } function renderSingleTable(active, expired, tableId) { const tableBody = document.getElementById(tableId); if ((!active || active.length === 0) && (!expired || expired.length === 0)) { tableBody.innerHTML = `

No certificates found

`; return; } let html = ''; // Render Active if (active && active.length > 0) { html += `Active Certificates (${active.length})`; active.forEach(cert => { html += generateRow(cert, false); }); } // Render Expired if (expired && expired.length > 0 && !hideExpired) { html += `Expired Certificates (${expired.length})`; expired.forEach(cert => { html += generateRow(cert, true); }); } tableBody.innerHTML = html; } function generateRow(cert, isExpired) { const commonName = cert.common_name || cert.subject || 'N/A'; const clientName = commonName.replace('CN=', '').trim(); let daysText = cert.days_remaining || 'N/A'; if (isExpired && daysText.includes('Expired')) { daysText = daysText.replace('Expired (', '').replace(' days ago)', '') + ' days ago'; } return `
${clientName}
${cert.file || 'N/A'}
${formatCertDate(cert.not_after)} ${daysText} ${getStatusBadge(cert.days_remaining)} `; } function updateSummaryStats(certificates) { const totalCertificates = document.getElementById('totalCertificates'); const activeCertificates = document.getElementById('activeCertificates'); const expiredCertificates = document.getElementById('expiredCertificates'); const expiringSoon = document.getElementById('expiringSoon'); const activeCount = document.getElementById('activeCount'); const expiredCount = document.getElementById('expiredCount'); const certCount = document.getElementById('certCount'); const { active, expired } = categorizeCertificates(certificates); let expiringSoonCount = 0; active.forEach(cert => { if (cert.days_remaining && cert.days_remaining !== 'N/A') { const days = parseInt(cert.days_remaining); if (days <= 30) { expiringSoonCount++; } } }); totalCertificates.textContent = certificates.length; activeCertificates.textContent = active.length; expiredCertificates.textContent = expired.length; expiringSoon.textContent = expiringSoonCount; // Update counts in badges document.getElementById('activeCount').textContent = active.length; document.getElementById('expiredCount').textContent = expired.length; certCount.textContent = certificates.length + ' certificate' + (certificates.length !== 1 ? 's' : ''); } function filterCertificates() { const searchTerm = document.getElementById('searchInput').value.toLowerCase(); let filteredCertificates = allCertificatesData; if (searchTerm) { filteredCertificates = allCertificatesData.filter(cert => { const commonName = (cert.common_name || cert.subject || '').toLowerCase(); const fileName = (cert.file || '').toLowerCase(); return commonName.includes(searchTerm) || fileName.includes(searchTerm); }); } const { active, expired } = categorizeCertificates(filteredCertificates); renderSingleTable(active, expired, 'certificatesTable'); updateSummaryStats(filteredCertificates); } function toggleExpiredCertificates() { hideExpired = document.getElementById('hideExpired').checked; const expiredCard = document.getElementById('certificatesCard'); // We just re-render filterCertificates(); } async function fetchData() { document.getElementById('refreshIcon').classList.add('refresh-indicator'); try { const response = await fetch(apiUrl); const json = await response.json(); if (json.success) { allCertificatesData = json.data; filterCertificates(); // This also renders tables } } catch (e) { console.error("Fetch error:", e); } finally { document.getElementById('refreshIcon').classList.remove('refresh-indicator'); } } // Ensure functionality is available for HTML event attributes window.fetchData = fetchData; window.filterCertificates = filterCertificates; window.toggleExpiredCertificates = toggleExpiredCertificates; document.addEventListener('DOMContentLoaded', () => { initTheme(); fetchData(); refreshInterval = setInterval(fetchData, refreshTime); });