move legacy UI in .php to artifacts
This commit is contained in:
228
UI/artifacts/js/pages/certificates.js
Normal file
228
UI/artifacts/js/pages/certificates.js
Normal file
@@ -0,0 +1,228 @@
|
||||
// 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 '<span class="status-badge text-muted">Unknown</span>';
|
||||
}
|
||||
|
||||
if (daysRemaining.includes('Expired')) {
|
||||
return '<span class="status-badge status-expired"><i class="fas fa-times-circle me-1"></i>Expired</span>';
|
||||
} else if (daysRemaining.includes('days')) {
|
||||
const days = parseInt(daysRemaining);
|
||||
if (days <= 30) {
|
||||
return '<span class="status-badge status-expiring"><i class="fas fa-exclamation-triangle me-1"></i>Expiring Soon</span>';
|
||||
} else {
|
||||
return '<span class="status-badge status-valid"><i class="fas fa-check-circle me-1"></i>Valid</span>';
|
||||
}
|
||||
}
|
||||
return '<span class="status-badge text-muted">Unknown</span>';
|
||||
}
|
||||
|
||||
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 = `
|
||||
<tr>
|
||||
<td colspan="4" class="empty-state">
|
||||
<i class="fas fa-certificate"></i>
|
||||
<p>No certificates found</p>
|
||||
</td>
|
||||
</tr>
|
||||
`;
|
||||
return;
|
||||
}
|
||||
|
||||
let html = '';
|
||||
|
||||
// Render Active
|
||||
if (active && active.length > 0) {
|
||||
html += `<tr class="section-divider"><td colspan="4">Active Certificates (${active.length})</td></tr>`;
|
||||
active.forEach(cert => {
|
||||
html += generateRow(cert, false);
|
||||
});
|
||||
}
|
||||
|
||||
// Render Expired
|
||||
if (expired && expired.length > 0 && !hideExpired) {
|
||||
html += `<tr class="section-divider"><td colspan="4">Expired Certificates (${expired.length})</td></tr>`;
|
||||
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 `
|
||||
<tr>
|
||||
<td>
|
||||
<div class="fw-semibold">${clientName}</div>
|
||||
<div class="certificate-file">${cert.file || 'N/A'}</div>
|
||||
</td>
|
||||
<td>${formatCertDate(cert.not_after)}</td>
|
||||
<td class="fw-semibold ${isExpired ? 'text-danger' :
|
||||
(daysText !== 'N/A' && parseInt(daysText) <= 30 ? 'text-warning' : 'text-success')
|
||||
}">
|
||||
${daysText}
|
||||
</td>
|
||||
<td>${getStatusBadge(cert.days_remaining)}</td>
|
||||
</tr>
|
||||
`;
|
||||
}
|
||||
|
||||
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);
|
||||
});
|
||||
Reference in New Issue
Block a user