// 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);
});