app.js 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. const API_BASE = window.location.origin;
  2. function getStateClass(state) {
  3. switch (state) {
  4. case 'running': return 'bg-success';
  5. case 'shut off': return 'bg-secondary';
  6. case 'paused': return 'bg-warning text-dark';
  7. case 'crashed': return 'bg-danger';
  8. default: return 'bg-light text-dark';
  9. }
  10. }
  11. async function loadISOs() {
  12. try {
  13. const response = await fetch(`${API_BASE}/isos`);
  14. const data = await response.json();
  15. const tbody = document.getElementById('isoTableBody');
  16. tbody.innerHTML = '';
  17. data.isos.forEach(iso => {
  18. const row = document.createElement('tr');
  19. row.innerHTML = `
  20. <td>${iso.name}</td>
  21. <td>
  22. <button class="btn btn-sm btn-danger" onclick="deleteISO('${iso.name}')">Delete</button>
  23. </td>
  24. `;
  25. tbody.appendChild(row);
  26. });
  27. } catch (error) {
  28. console.error('Error loading ISOs:', error);
  29. alert('Error loading ISOs');
  30. }
  31. }
  32. async function deleteISO(name) {
  33. if (confirm(`Delete ISO ${name}?`)) {
  34. try {
  35. const response = await fetch(`${API_BASE}/isos/${name}`, { method: 'DELETE' });
  36. const result = await response.json();
  37. if (response.ok) {
  38. alert(result.message);
  39. loadISOs();
  40. loadISOsForSelect(); // Refresh the select
  41. } else {
  42. alert(result.error);
  43. }
  44. } catch (error) {
  45. console.error('Error deleting ISO:', error);
  46. alert('Error deleting ISO');
  47. }
  48. }
  49. }
  50. async function loadISOsForSelect() {
  51. try {
  52. const response = await fetch(`${API_BASE}/isos`);
  53. const data = await response.json();
  54. const select = document.getElementById('iso');
  55. select.innerHTML = '<option value="">Select ISO</option>';
  56. data.isos.forEach(iso => {
  57. select.innerHTML += `<option value="${iso.path}">${iso.name}</option>`;
  58. });
  59. } catch (error) {
  60. console.error('Error loading ISOs:', error);
  61. }
  62. }
  63. async function loadVMs() {
  64. try {
  65. const response = await fetch(`${API_BASE}/vms`);
  66. const data = await response.json();
  67. const tbody = document.getElementById('vmTableBody');
  68. tbody.innerHTML = '';
  69. const select = document.getElementById('bootVM');
  70. select.innerHTML = '<option value="">Select VM</option>';
  71. data.vms.forEach(vm => {
  72. const row = document.createElement('tr');
  73. row.innerHTML = `
  74. <td>${vm.id}</td>
  75. <td>${vm.name}</td>
  76. <td><span class="badge ${getStateClass(vm.state)}">${vm.state}</span></td>
  77. <td>
  78. <button class="btn btn-sm btn-success" onclick="startVM('${vm.name}')">Lanzar</button>
  79. <button class="btn btn-sm btn-warning" onclick="stopVM('${vm.name}')">Stop</button>
  80. <button class="btn btn-sm btn-danger" onclick="deleteVM('${vm.name}')">Delete</button>
  81. ${vm.vnc_port ? `<button class="btn btn-sm btn-info" onclick="openVNC('${vm.name}')">Console</button>` : ''}
  82. </td>
  83. `;
  84. tbody.appendChild(row);
  85. select.innerHTML += `<option value="${vm.name}">${vm.name}</option>`;
  86. });
  87. loadISOsForSelect(); // Load ISOs after loading VMs
  88. } catch (error) {
  89. console.error('Error loading VMs:', error);
  90. alert('Error loading VMs');
  91. }
  92. }
  93. async function startVM(name) {
  94. try {
  95. await fetch(`${API_BASE}/vms/${name}/start`);
  96. loadVMs();
  97. } catch (error) {
  98. console.error('Error starting VM:', error);
  99. alert('Error starting VM');
  100. }
  101. }
  102. async function stopVM(name) {
  103. try {
  104. await fetch(`${API_BASE}/vms/${name}/stop`);
  105. loadVMs();
  106. } catch (error) {
  107. console.error('Error stopping VM:', error);
  108. alert('Error stopping VM');
  109. }
  110. }
  111. async function deleteVM(name) {
  112. if (confirm(`Delete VM ${name}?`)) {
  113. try {
  114. await fetch(`${API_BASE}/vms/${name}`, { method: 'DELETE' });
  115. loadVMs();
  116. } catch (error) {
  117. console.error('Error deleting VM:', error);
  118. alert('Error deleting VM');
  119. }
  120. }
  121. }
  122. function openVNC(vm_name) {
  123. fetch(`${API_BASE}/console/${vm_name}`)
  124. .then(response => response.json())
  125. .then(data => {
  126. if (data.url) {
  127. window.open(data.url);
  128. } else {
  129. alert(data.error || 'Error opening console');
  130. }
  131. })
  132. .catch(error => {
  133. console.error('Error:', error);
  134. alert('Error opening console');
  135. });
  136. }
  137. document.getElementById('createVMForm').addEventListener('submit', async (e) => {
  138. e.preventDefault();
  139. const name = document.getElementById('name').value;
  140. const memory = document.getElementById('memory').value;
  141. const disk_size = document.getElementById('disk_size').value;
  142. const iso = document.getElementById('iso').value;
  143. try {
  144. const response = await fetch(`${API_BASE}/vms?name=${name}&memory=${memory}&disk_size=${disk_size}&iso=${iso}`, {
  145. method: 'POST'
  146. });
  147. let result;
  148. try {
  149. result = await response.json();
  150. } catch {
  151. result = { error: 'Unknown error' };
  152. }
  153. if (response.ok) {
  154. alert(result.message);
  155. loadVMs();
  156. document.getElementById('createVMForm').reset();
  157. } else {
  158. alert(result.error || 'Error creating VM');
  159. }
  160. } catch (error) {
  161. console.error('Error creating VM:', error);
  162. alert('Error creating VM');
  163. }
  164. });
  165. document.getElementById('setBootOrderForm').addEventListener('submit', async (e) => {
  166. e.preventDefault();
  167. const vm_name = document.getElementById('bootVM').value;
  168. const boot_order_str = document.getElementById('bootOrder').value;
  169. const order = boot_order_str.split(',').map(s => s.trim());
  170. try {
  171. const response = await fetch(`${API_BASE}/vms/${vm_name}/boot-order`, {
  172. method: 'PUT',
  173. headers: {
  174. 'Content-Type': 'application/json'
  175. },
  176. body: JSON.stringify({ order: order })
  177. });
  178. const result = await response.json();
  179. if (response.ok) {
  180. alert(result.message);
  181. loadVMs();
  182. document.getElementById('setBootOrderForm').reset();
  183. } else {
  184. alert(result.error);
  185. }
  186. } catch (error) {
  187. console.error('Error setting boot order:', error);
  188. alert('Error setting boot order');
  189. }
  190. });
  191. document.getElementById('uploadIsoForm').addEventListener('submit', async (e) => {
  192. e.preventDefault();
  193. const fileInput = document.getElementById('isoFile');
  194. const file = fileInput.files[0];
  195. if (!file) {
  196. alert('Please select a file');
  197. return;
  198. }
  199. const formData = new FormData();
  200. formData.append('file', file);
  201. try {
  202. const response = await fetch(`${API_BASE}/upload-iso`, {
  203. method: 'POST',
  204. body: formData
  205. });
  206. const result = await response.json();
  207. if (response.ok) {
  208. alert(result.message);
  209. loadISOsForSelect(); // Refresh ISO list
  210. document.getElementById('uploadIsoForm').reset();
  211. } else {
  212. alert(result.error || 'Error uploading ISO');
  213. }
  214. } catch (error) {
  215. console.error('Error uploading ISO:', error);
  216. alert('Error uploading ISO');
  217. }
  218. });
  219. // Load VMs on page load
  220. loadVMs();
  221. loadISOs();