const API_BASE = window.location.origin; let selectedVM = null; // Initialize dashboard document.addEventListener('DOMContentLoaded', () => { loadVMs(); loadISOsForSelect(); // Initialize sidebar state for mobile const sidebar = document.querySelector('.sidebar'); if (window.innerWidth <= 768) { sidebar.classList.add('hidden-mobile'); } // Create VM Form document.getElementById('createVMForm').addEventListener('submit', createVM); }); // Get badge class based on VM state function getStateClass(state) { switch (state) { case 'running': return 'badge-running'; case 'shut off': return 'badge-off'; case 'paused': return 'badge-paused'; case 'crashed': return 'badge-error'; default: return 'bg-light text-dark'; } } // Get icon for state function getStateIcon(state) { switch (state) { case 'running': return ''; case 'shut off': return ''; case 'paused': return ''; default: return ''; } } // Load VMs from API async function loadVMs() { try { const response = await fetch(`${API_BASE}/vms`); const data = await response.json(); const vmList = document.getElementById('vmList'); if (!data.vms || data.vms.length === 0) { vmList.innerHTML = '

No hay máquinas virtuales

'; return; } vmList.innerHTML = data.vms.map(vm => `
${vm.name}
${vm.id !== -1 ? 'ID: ' + vm.id : 'Detenida'}
${vm.state}
`).join(''); } catch (error) { console.error('Error loading VMs:', error); } } // Select a VM and show details async function selectVM(vmName) { selectedVM = vmName; loadVMs(); // Refresh to highlight selected try { const response = await fetch(`${API_BASE}/vms/${vmName}/info`); const vm = await response.json(); if (vm.error) { showError(vm.error); return; } displayVMDetails(vm, vmName); } catch (error) { console.error('Error loading VM info:', error); showError('Error al cargar información de la VM'); } } // Display VM details function displayVMDetails(vm, vmName) { const contentArea = document.getElementById('contentArea'); const topBar = document.getElementById('currentVMName'); const topBarActions = document.getElementById('topBarActions'); topBar.textContent = vmName; // Build status badge const statusBadge = `${vm.state}`; // Build action buttons const actionButtons = `
${vm.state === 'running' ? ` ` : ` `}
`; topBarActions.innerHTML = actionButtons; // Build content const disksHTML = vm.disks && vm.disks.length > 0 ? vm.disks.map(disk => `
  • ${disk.target} (${disk.driver}) - ${disk.source}
  • `).join('') : '
  • Sin discos
  • '; const interfacesHTML = vm.interfaces && vm.interfaces.length > 0 ? vm.interfaces.map(iface => `
  • MAC: ${iface.mac}
    Red: ${iface.network}
  • `).join('') : '
  • Sin interfaces
  • '; contentArea.innerHTML = `
    Configuración
    ID de Dominio
    ${vm.id !== -1 ? vm.id : 'N/A (Parada)'}
    Estado
    ${statusBadge}
    Procesadores
    ${vm.cpus} CPU${vm.cpus !== 1 ? 's' : ''}
    Memoria RAM
    ${vm.memory} MiB
    Display
    Puerto VNC
    ${vm.vnc_port ? vm.vnc_port : 'No asignado'}
    Dirección
    ${vm.vnc_port ? `localhost:${vm.vnc_port}` : 'N/A'}
    Acciones
    Discos
      ${disksHTML}
    Interfaces de Red
      ${interfacesHTML}
    `; } // Show error message function showError(message) { const contentArea = document.getElementById('contentArea'); contentArea.innerHTML = ` `; } // Open Create VM Modal function openCreateVMModal() { const modal = new bootstrap.Modal(document.getElementById('createVMModal')); modal.show(); } // Create VM async function createVM(e) { e.preventDefault(); const name = document.getElementById('name').value; const memory = document.getElementById('memory').value; const disk_size = document.getElementById('disk_size').value; const cpus = document.getElementById('cpus').value; const iso = document.getElementById('iso').value; if (!iso) { alert('Por favor selecciona una ISO'); return; } try { const response = await fetch(`${API_BASE}/vms?name=${name}&memory=${memory}&disk_size=${disk_size}&cpus=${cpus}&iso=${iso}`, { method: 'POST' }); let result; try { result = await response.json(); } catch { result = { error: `Server error: ${response.status}` }; } if (response.ok) { alert(result.message || 'VM creada correctamente'); document.getElementById('createVMForm').reset(); bootstrap.Modal.getInstance(document.getElementById('createVMModal')).hide(); loadVMs(); } else { alert(result.error || 'Error al crear VM'); } } catch (error) { console.error('Error:', error); alert(`Error: ${error.message}`); } } // Load ISOs for select async function loadISOsForSelect() { try { const response = await fetch(`${API_BASE}/isos`); const data = await response.json(); const select = document.getElementById('iso'); select.innerHTML = ''; if (data.isos && data.isos.length > 0) { data.isos.forEach(iso => { select.innerHTML += ``; }); } } catch (error) { console.error('Error loading ISOs:', error); } } // Start VM async function startVM(vmName) { try { const response = await fetch(`${API_BASE}/vms/${vmName}/start`); const result = await response.json(); if (response.ok) { loadVMs(); if (selectedVM === vmName) { selectVM(vmName); } } else { alert(result.error || 'Error al iniciar VM'); } } catch (error) { console.error('Error:', error); alert(`Error: ${error.message}`); } } // Stop VM async function stopVM(vmName) { if (confirm(`¿Estás seguro de que quieres detener ${vmName}?`)) { try { const response = await fetch(`${API_BASE}/vms/${vmName}/stop`); const result = await response.json(); if (response.ok) { loadVMs(); if (selectedVM === vmName) { selectVM(vmName); } } else { alert(result.error || 'Error al detener VM'); } } catch (error) { console.error('Error:', error); alert(`Error: ${error.message}`); } } } // Delete VM async function deleteVMConfirm(vmName) { if (confirm(`¿Estás seguro de que quieres eliminar ${vmName}? Esta acción no se puede deshacer.`)) { try { const response = await fetch(`${API_BASE}/vms/${vmName}`, { method: 'DELETE' }); const result = await response.json(); if (response.ok) { alert('VM eliminada correctamente'); selectedVM = null; loadVMs(); document.getElementById('contentArea').innerHTML = `
    Selecciona una máquina virtual

    Haz clic en una VM del panel lateral para ver sus detalles

    `; } else { alert(result.error || 'Error al eliminar VM'); } } catch (error) { console.error('Error:', error); alert(`Error: ${error.message}`); } } } // Open Boot Order Modal function openBootOrderModal(vmName) { const html = ` `; document.body.insertAdjacentHTML('beforeend', html); const modal = new bootstrap.Modal(document.getElementById('bootOrderModal')); document.getElementById('bootOrderForm').addEventListener('submit', async (e) => { e.preventDefault(); const order = document.getElementById('bootOrderInput').value.split(',').map(s => s.trim()); try { const response = await fetch(`${API_BASE}/vms/${vmName}/boot-order`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ order }) }); const result = await response.json(); if (response.ok) { alert('Orden de arranque actualizado'); modal.hide(); document.getElementById('bootOrderModal').remove(); } else { alert(result.error || 'Error al actualizar'); } } catch (error) { alert(`Error: ${error.message}`); } }); modal.show(); } // Open Add Disk Modal function openAddDiskModal(vmName) { const html = ` `; document.body.insertAdjacentHTML('beforeend', html); const modal = new bootstrap.Modal(document.getElementById('addDiskModal')); document.getElementById('addDiskFormModal').addEventListener('submit', async (e) => { e.preventDefault(); const disk_name = document.getElementById('diskNameInput').value; const disk_size = document.getElementById('diskSizeInput').value; try { const response = await fetch(`${API_BASE}/vms/${vmName}/add-disk?disk_name=${disk_name}&disk_size=${disk_size}`, { method: 'POST' }); let result; try { result = await response.json(); } catch { result = { error: 'Error del servidor' }; } if (response.ok) { alert(result.message || 'Disco agregado correctamente'); modal.hide(); document.getElementById('addDiskModal').remove(); selectVM(vmName); // Refresh } else { alert(result.error || 'Error al agregar disco'); } } catch (error) { alert(`Error: ${error.message}`); } }); modal.show(); } // Open Console function openConsole(vmName) { fetch(`${API_BASE}/console/${vmName}`) .then(response => response.json()) .then(data => { if (data.url) { window.open(data.url); } else { alert(data.error || 'Error al abrir consola'); } }) .catch(error => { console.error('Error:', error); alert('Error al abrir consola'); }); } // Open ISO Management Modal function openISOManagementModal() { const modal = new bootstrap.Modal(document.getElementById('isoManagementModal')); loadISOs(); setupUploadISOForm(); modal.show(); } // Load ISOs for management async function loadISOs() { try { const response = await fetch(`${API_BASE}/isos`); const data = await response.json(); const tbody = document.getElementById('isoTableBody'); if (!data.isos || data.isos.length === 0) { tbody.innerHTML = 'No hay ISOs subidas'; return; } tbody.innerHTML = data.isos.map(iso => ` ${iso.name} `).join(''); } catch (error) { console.error('Error loading ISOs:', error); document.getElementById('isoTableBody').innerHTML = 'Error al cargar ISOs'; } } // Setup ISO upload form function setupUploadISOForm() { const form = document.getElementById('uploadIsoForm'); if (!form.dataset.listenerAdded) { form.addEventListener('submit', uploadISO); form.dataset.listenerAdded = 'true'; } } // Upload ISO async function uploadISO(e) { e.preventDefault(); const isoFile = document.getElementById('isoFile'); const uploadBtn = document.getElementById('uploadBtn'); const progressContainer = document.getElementById('progressContainer'); const uploadProgress = document.getElementById('uploadProgress'); if (!isoFile.files.length) { alert('Por favor selecciona un archivo'); return; } const formData = new FormData(); formData.append('file', isoFile.files[0]); uploadBtn.disabled = true; progressContainer.style.display = 'block'; try { const xhr = new XMLHttpRequest(); xhr.upload.addEventListener('progress', (e) => { if (e.lengthComputable) { const percentComplete = (e.loaded / e.total) * 100; uploadProgress.style.width = percentComplete + '%'; uploadProgress.setAttribute('aria-valuenow', percentComplete); uploadProgress.textContent = Math.round(percentComplete) + '%'; } }); xhr.addEventListener('load', () => { if (xhr.status === 200) { const result = JSON.parse(xhr.responseText); alert(result.message || 'ISO subida correctamente'); document.getElementById('uploadIsoForm').reset(); progressContainer.style.display = 'none'; uploadProgress.style.width = '0%'; uploadProgress.textContent = '0%'; loadISOs(); loadISOsForSelect(); } else { const result = JSON.parse(xhr.responseText); alert(result.error || 'Error al subir ISO'); progressContainer.style.display = 'none'; } uploadBtn.disabled = false; }); xhr.addEventListener('error', () => { alert('Error al subir ISO'); progressContainer.style.display = 'none'; uploadBtn.disabled = false; }); xhr.open('POST', `${API_BASE}/upload-iso`); xhr.send(formData); } catch (error) { console.error('Error:', error); alert(`Error: ${error.message}`); uploadBtn.disabled = false; progressContainer.style.display = 'none'; } } // Delete ISO async function deleteISO(name) { if (confirm(`¿Estás seguro de que quieres eliminar ${name}?`)) { try { const response = await fetch(`${API_BASE}/isos/${name}`, { method: 'DELETE' }); const result = await response.json(); if (response.ok) { alert('ISO eliminada correctamente'); loadISOs(); loadISOsForSelect(); } else { alert(result.error || 'Error al eliminar ISO'); } } catch (error) { console.error('Error:', error); alert(`Error: ${error.message}`); } } } // Mobile responsive - Toggle sidebar function toggleSidebar() { const sidebar = document.querySelector('.sidebar'); // Para móviles (max-width 768px), mostrar/ocultar el sidebar if (window.innerWidth <= 768) { sidebar.classList.toggle('show'); } } // Cerrar sidebar cuando se selecciona una VM en móvil const originalSelectVM = window.selectVM; window.selectVM = function(vmName) { originalSelectVM(vmName); // Cerrar sidebar en móvil después de seleccionar if (window.innerWidth <= 768) { const sidebar = document.querySelector('.sidebar'); sidebar.classList.remove('show'); } }; // Cerrar sidebar cuando se abre un modal document.addEventListener('shown.bs.modal', function() { if (window.innerWidth <= 768) { const sidebar = document.querySelector('.sidebar'); sidebar.classList.remove('show'); } }); // Download VM configuration as XML async function downloadVMConfig(vmName) { try { const response = await fetch(`${API_BASE}/vms/${vmName}/config`); if (!response.ok) { alert(`Error: El servidor respondió con estado ${response.status}`); return; } const data = await response.json(); if (data.error) { alert(`Error: ${data.error}`); return; } // Validar que tenemos el contenido XML if (!data.xml || data.xml === undefined || data.xml === 'undefined') { console.error('XML content is undefined or missing:', data); alert('Error: No se pudo obtener la configuración XML'); return; } // Create a blob with the XML content const blob = new Blob([data.xml], { type: 'application/xml' }); const url = window.URL.createObjectURL(blob); const link = document.createElement('a'); link.href = url; link.download = `${vmName}-config.xml`; document.body.appendChild(link); link.click(); document.body.removeChild(link); window.URL.revokeObjectURL(url); } catch (error) { console.error('Error downloading config:', error); alert(`Error al descargar configuración: ${error.message}`); } }