/** * VHost Manager - Frontend CRUD Logic */ let currentEditVHostId = null; document.addEventListener('DOMContentLoaded', function() { loadVHosts(); // Event listeners document.getElementById('createVHostBtn').addEventListener('click', createVHost); document.getElementById('updateVHostBtn').addEventListener('click', updateVHost); document.getElementById('addServerBtn').addEventListener('click', addServerInput); // Event delegation for remove buttons document.getElementById('backendServersContainer').addEventListener('click', function(e) { if (e.target.classList.contains('remove-server')) { e.target.closest('.backend-server').remove(); } }); }); function loadVHosts() { fetch('/api/vhosts') .then(r => r.json()) .then(data => { if (data.success) { renderVHosts(data.vhosts); updateStats(data.vhosts); } else { showAlert(data.error, 'danger'); } }) .catch(e => console.error('Error loading vhosts:', e)); } function updateStats(vhosts) { document.getElementById('total_vhosts').textContent = vhosts.length; document.getElementById('enabled_vhosts').textContent = vhosts.filter(v => v.enabled).length; document.getElementById('disabled_vhosts').textContent = vhosts.filter(v => !v.enabled).length; document.getElementById('ssl_vhosts').textContent = vhosts.filter(v => v.use_ssl).length; } function renderVHosts(vhosts) { const tbody = document.getElementById('vhostsList'); if (vhosts.length === 0) { tbody.innerHTML = 'No VHosts created yet'; return; } tbody.innerHTML = vhosts.map(v => ` ${escapeHtml(v.name)} ${v.use_ssl ? 'SSL' : ''} ${escapeHtml(v.hostname)} ${v.frontend_ip}:${v.frontend_port} ${v.protocol.toUpperCase()} ${v.backend_count} servers `).join(''); } function createVHost() { const name = document.getElementById('vhost_name').value; const hostname = document.getElementById('vhost_hostname').value; const port = document.getElementById('vhost_port').value; const protocol = document.getElementById('vhost_protocol').value; const lb_method = document.getElementById('vhost_lb_method').value; const use_ssl = document.getElementById('vhost_ssl').checked; // Get backend servers const servers = []; document.querySelectorAll('.backend-server').forEach(el => { const ip = el.querySelector('.backend-ip').value; const port = el.querySelector('.backend-port').value; if (ip && port) { servers.push({ ip_address: ip, port: parseInt(port) }); } }); if (servers.length === 0) { showAlert('At least one backend server is required', 'warning'); return; } const payload = { name, hostname, frontend_port: parseInt(port), protocol, lb_method, use_ssl, backend_servers: servers }; fetch('/api/vhosts', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify(payload) }) .then(r => r.json()) .then(data => { if (data.success) { bootstrap.Modal.getInstance(document.getElementById('newVHostModal')).hide(); document.getElementById('newVHostForm').reset(); document.getElementById('backendServersContainer').innerHTML = `
`; loadVHosts(); showAlert('VHost created successfully', 'success'); } else { showAlert(data.error, 'danger'); } }) .catch(e => showAlert(e.message, 'danger')); } function editVHost(vhostId) { currentEditVHostId = vhostId; fetch(`/api/vhosts/${vhostId}`) .then(r => r.json()) .then(data => { if (data.success) { const v = data.vhost; document.getElementById('editVHostName').textContent = v.name; const formContent = `

Backend Servers
${v.backend_servers.map(bs => `
`).join('')}
`; document.getElementById('editFormContent').innerHTML = formContent; new bootstrap.Modal(document.getElementById('editVHostModal')).show(); } }) .catch(e => showAlert(e.message, 'danger')); } function updateVHost() { const name = document.getElementById('edit_name').value; const hostname = document.getElementById('edit_hostname').value; const enabled = document.getElementById('edit_enabled').checked; const payload = { name, hostname, enabled }; fetch(`/api/vhosts/${currentEditVHostId}`, { method: 'PUT', headers: {'Content-Type': 'application/json'}, body: JSON.stringify(payload) }) .then(r => r.json()) .then(data => { if (data.success) { bootstrap.Modal.getInstance(document.getElementById('editVHostModal')).hide(); loadVHosts(); showAlert('VHost updated successfully', 'success'); } else { showAlert(data.error, 'danger'); } }) .catch(e => showAlert(e.message, 'danger')); } function toggleVHost(vhostId, isEnabled) { fetch(`/api/vhosts/${vhostId}/toggle`, { method: 'POST' }) .then(r => r.json()) .then(data => { if (data.success) { loadVHosts(); showAlert(data.message, 'success'); } else { showAlert(data.error, 'danger'); } }) .catch(e => showAlert(e.message, 'danger')); } function deleteVHost(vhostId, name) { if (!confirm(`Delete VHost '${name}'? This cannot be undone.`)) return; fetch(`/api/vhosts/${vhostId}`, { method: 'DELETE' }) .then(r => r.json()) .then(data => { if (data.success) { loadVHosts(); showAlert('VHost deleted successfully', 'success'); } else { showAlert(data.error, 'danger'); } }) .catch(e => showAlert(e.message, 'danger')); } function addServerInput() { const container = document.getElementById('backendServersContainer'); const newServer = document.createElement('div'); newServer.className = 'backend-server mb-3 p-3 border rounded'; newServer.innerHTML = `
`; container.appendChild(newServer); } function showAlert(message, type) { const alertDiv = document.createElement('div'); alertDiv.className = `alert alert-${type} alert-dismissible fade show`; alertDiv.innerHTML = ` ${message} `; document.querySelector('.card:first-of-type').parentElement.insertBefore(alertDiv, document.querySelector('.card:first-of-type')); setTimeout(() => alertDiv.remove(), 5000); } function escapeHtml(text) { const map = {'&': '&', '<': '<', '>': '>', '"': '"', "'": '''}; return text.replace(/[&<>"']/g, m => map[m]); }