app.js 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  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 loadVMsCard() {
  94. try {
  95. const response = await fetch(`${API_BASE}/vms`);
  96. const data = await response.json();
  97. const card = document.getElementById('vmCard');
  98. card.innerHTML = '';
  99. // const select = document.getElementById('bootVM');
  100. //select.innerHTML = '<option value="">Select VM</option>';
  101. data.vms.forEach(vm => {
  102. const tarjeta = document.createElement('div');
  103. tarjeta.className = 'card-body';
  104. tarjeta.innerHTML = `
  105. <h6>${vm.id}</h6>
  106. <h5>${vm.name}</h5>
  107. <p><span class="badge ${getStateClass(vm.state)}">${vm.state}</span></p
  108. <button class="btn btn-sm btn-success" onclick="startVM('${vm.name}')">Lanzar</button>
  109. <button class="btn btn-sm btn-warning" onclick="stopVM('${vm.name}')">Stop</button>
  110. <button class="btn btn-sm btn-danger" onclick="deleteVM('${vm.name}')">Delete</button>
  111. ${vm.vnc_port ? `<button class="btn btn-sm btn-info" onclick="openVNC('${vm.name}')">Console</button>` : ''}
  112. `;
  113. card.appendChild(tarjeta);
  114. //select.innerHTML += `<option value="${vm.name}">${vm.name}</option>`;
  115. });
  116. //loadISOsForSelect(); // Load ISOs after loading VMs
  117. } catch (error) {
  118. console.error('Error loading VMs:', error);
  119. alert('Error loading VMs');
  120. }
  121. }
  122. async function startVM(name) {
  123. try {
  124. await fetch(`${API_BASE}/vms/${name}/start`);
  125. loadVMs();
  126. } catch (error) {
  127. console.error('Error starting VM:', error);
  128. alert('Error starting VM');
  129. }
  130. }
  131. async function stopVM(name) {
  132. try {
  133. await fetch(`${API_BASE}/vms/${name}/stop`);
  134. loadVMs();
  135. } catch (error) {
  136. console.error('Error stopping VM:', error);
  137. alert('Error stopping VM');
  138. }
  139. }
  140. async function deleteVM(name) {
  141. if (confirm(`Delete VM ${name}?`)) {
  142. try {
  143. await fetch(`${API_BASE}/vms/${name}`, { method: 'DELETE' });
  144. loadVMs();
  145. } catch (error) {
  146. console.error('Error deleting VM:', error);
  147. alert('Error deleting VM');
  148. }
  149. }
  150. }
  151. function openVNC(vm_name) {
  152. fetch(`${API_BASE}/console/${vm_name}`)
  153. .then(response => response.json())
  154. .then(data => {
  155. if (data.url) {
  156. window.open(data.url);
  157. } else {
  158. alert(data.error || 'Error opening console');
  159. }
  160. })
  161. .catch(error => {
  162. console.error('Error:', error);
  163. alert('Error opening console');
  164. });
  165. }
  166. document.getElementById('createVMForm').addEventListener('submit', async (e) => {
  167. e.preventDefault();
  168. const name = document.getElementById('name').value;
  169. const memory = document.getElementById('memory').value;
  170. const disk_size = document.getElementById('disk_size').value;
  171. const iso = document.getElementById('iso').value;
  172. try {
  173. const response = await fetch(`${API_BASE}/vms?name=${name}&memory=${memory}&disk_size=${disk_size}&iso=${iso}`, {
  174. method: 'POST'
  175. });
  176. let result;
  177. try {
  178. result = await response.json();
  179. } catch {
  180. result = { error: 'Unknown error' };
  181. }
  182. if (response.ok) {
  183. alert(result.message);
  184. loadVMs();
  185. document.getElementById('createVMForm').reset();
  186. } else {
  187. alert(result.error || 'Error creating VM');
  188. }
  189. } catch (error) {
  190. console.error('Error creating VM:', error);
  191. alert('Error creating VM');
  192. }
  193. });
  194. document.getElementById('setBootOrderForm').addEventListener('submit', async (e) => {
  195. e.preventDefault();
  196. const vm_name = document.getElementById('bootVM').value;
  197. const boot_order_str = document.getElementById('bootOrder').value;
  198. const order = boot_order_str.split(',').map(s => s.trim());
  199. try {
  200. const response = await fetch(`${API_BASE}/vms/${vm_name}/boot-order`, {
  201. method: 'PUT',
  202. headers: {
  203. 'Content-Type': 'application/json'
  204. },
  205. body: JSON.stringify({ order: order })
  206. });
  207. const result = await response.json();
  208. if (response.ok) {
  209. alert(result.message);
  210. loadVMs();
  211. document.getElementById('setBootOrderForm').reset();
  212. } else {
  213. alert(result.error);
  214. }
  215. } catch (error) {
  216. console.error('Error setting boot order:', error);
  217. alert('Error setting boot order');
  218. }
  219. });
  220. document.getElementById('uploadIsoForm').addEventListener('submit', async (e) => {
  221. e.preventDefault();
  222. const fileInput = document.getElementById('isoFile');
  223. const file = fileInput.files[0];
  224. if (!file) {
  225. alert('Please select a file');
  226. return;
  227. }
  228. const formData = new FormData();
  229. formData.append('file', file);
  230. try {
  231. const response = await fetch(`${API_BASE}/upload-iso`, {
  232. method: 'POST',
  233. body: formData
  234. });
  235. const result = await response.json();
  236. if (response.ok) {
  237. alert(result.message);
  238. loadISOsForSelect(); // Refresh ISO list
  239. document.getElementById('uploadIsoForm').reset();
  240. } else {
  241. alert(result.error || 'Error uploading ISO');
  242. }
  243. } catch (error) {
  244. console.error('Error uploading ISO:', error);
  245. alert('Error uploading ISO');
  246. }
  247. });
  248. // Load VMs on page load
  249. loadVMs();
  250. loadVMsCard();
  251. loadISOs();