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