This commit is contained in:
Lizandro Guarnizo
2026-05-21 22:37:31 -05:00
parent 154a2fa245
commit 0cf7c6d279
7 changed files with 218 additions and 2 deletions
+50
View File
@@ -79,6 +79,13 @@
d="m14.74 9-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 0 1-2.244 2.077H8.084a2.25 2.25 0 0 1-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 0 0-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 0 1 3.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 0 0-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 0 0-7.5 0" />
</svg>
</button>
<button x-show="data.hostinger_vps_id" @click="syncHostinger(data.id)"
:disabled="syncCargando[data.id]" title="Sincronizar desde Hostinger"
class="text-orange-500 hover:text-orange-700 disabled:opacity-40">
<svg :class="syncCargando[data.id] ? 'animate-spin' : ''" class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"/>
</svg>
</button>
</div>
</td>
</tr>
@@ -262,6 +269,21 @@
</div>
</div>
<div class="flex flex-col sm:flex-row justify-between gap-4 mb-4">
<div class="w-full">
<label class="block mb-2">Hostinger VPS ID <span class="text-xs text-gray-400">(opcional)</span>:</label>
<input type="number" x-model="selectedItem.hostinger_vps_id"
class="border border-gray-300 rounded p-2 w-full" placeholder="ID del VPS en Hostinger" />
</div>
<div class="w-full flex items-end" x-show="selectedItem.id && selectedItem.hostinger_vps_id">
<button @click="syncHostinger(selectedItem.id)" :disabled="syncCargando[selectedItem.id]"
class="w-full px-4 py-2 bg-orange-500 text-white rounded text-sm flex items-center justify-center gap-2 hover:bg-orange-600 disabled:opacity-50">
<svg :class="syncCargando[selectedItem.id] ? 'animate-spin' : ''" class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"/></svg>
<span x-text="syncCargando[selectedItem.id] ? 'Sincronizando...' : 'Sync Hostinger'"></span>
</button>
</div>
</div>
<div class="flex justify-between gap-2 text-sm">
<button @click="updateItem()" class="px-4 py-2 bg-[#8eb02f] text-white rounded">Actualizar</button>
<button @click="closeModals()" class="px-4 py-2 bg-gray-600 text-white rounded">Cerrar</button>
@@ -344,6 +366,14 @@
</div>
</div>
<div class="flex flex-col sm:flex-row justify-between gap-4 mb-4">
<div class="w-full">
<label class="block mb-2">Hostinger VPS ID <span class="text-xs text-gray-400">(opcional)</span>:</label>
<input type="number" x-model="selectedItem.hostinger_vps_id"
class="border border-gray-300 rounded p-2 w-full" placeholder="ID del VPS en Hostinger" />
</div>
</div>
<div class="flex justify-between gap-2 text-sm">
<button @click="addItem()" class="px-4 py-2 bg-[#8eb02f] text-white rounded">Guardar</button>
<button @click="addModal = false" class="px-4 py-2 bg-gray-600 text-white rounded">Cancelar</button>
@@ -386,6 +416,7 @@
deleteModal: false,
selectedItem: {},
errors: {},
syncCargando: {},
init() {
this.loadData();
this.loadProvServidor();
@@ -421,6 +452,8 @@
ultimo_ping: data.ultimo_ping,
prov_servidor_id: data.prov_servidor_id,
tipo_servidor_id: data.tipo_servidor_id,
hostinger_vps_id: data.hostinger_vps_id,
hostinger_state: data.hostinger_state,
created_at: data.CreatedAt,
}));
console.log('📊 Servidores procesados:', this.datos.length);
@@ -552,6 +585,22 @@
});
},
async syncHostinger(id) {
if (!id || this.syncCargando[id]) return;
this.syncCargando[id] = true;
try {
const r = await axios.post(`/app/servidor/${id}/sync-hostinger`);
if (r.data?.ok) {
await this.loadData();
alert('Sincronizado desde Hostinger correctamente.');
}
} catch (e) {
alert('Error al sincronizar: ' + (e.response?.data?.error || e.message));
} finally {
this.syncCargando[id] = false;
}
},
updateItem() {
if (this.selectedItem.prov_servidor_id) {
this.selectedItem.prov_servidor_id = parseInt(this.selectedItem.prov_servidor_id);
@@ -575,6 +624,7 @@
ultimo_ping: this.selectedItem.ultimo_ping,
prov_servidor_id: this.selectedItem.prov_servidor_id,
tipo_servidor_id: this.selectedItem.tipo_servidor_id,
hostinger_vps_id: this.selectedItem.hostinger_vps_id ? parseInt(this.selectedItem.hostinger_vps_id) : null,
};
this.setLoading(true);
+33 -2
View File
@@ -46,8 +46,11 @@
<span x-show="agentOnline(servidor)">● En línea</span>
<span x-show="!agentOnline(servidor) && servidor.agent_token">● Fuera</span>
<span x-show="!servidor.agent_token">○ Sin agente</span>
</span>
</div>
</span> <!-- Badge estado Hostinger -->
<span x-show="servidor.hostinger_vps_id" class="text-xs px-2 py-0.5 rounded-full font-bold"
:class="servidor.hostinger_state === 'running' ? 'bg-emerald-200 text-emerald-900' : (servidor.hostinger_state ? 'bg-amber-200 text-amber-900' : 'bg-slate-200 text-slate-600')">
<span x-text="servidor.hostinger_state || 'Hostinger'" class="capitalize"></span>
</span> </div>
</div>
</div>
@@ -153,6 +156,14 @@
</svg>
Agente
</button>
<button x-show="servidor.hostinger_vps_id" @click="syncHostinger(servidor)"
:disabled="syncCargando[servidor.ID]"
title="Sincronizar specs desde Hostinger"
class="px-3 py-2 rounded-lg font-medium transition-colors text-sm border border-orange-300 text-orange-600 hover:bg-orange-50 flex items-center gap-1 disabled:opacity-40">
<svg :class="syncCargando[servidor.ID] ? 'animate-spin' : ''" class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"/>
</svg>
</button>
</div>
</div>
</div>
@@ -484,6 +495,7 @@
agentTokenCargando: false,
cargando: true,
cargandoConexiones: false,
syncCargando: {},
pingCargando: {},
pingResultado: {},
@@ -560,6 +572,25 @@
}
},
async syncHostinger(servidor) {
const id = servidor.ID;
if (!id || this.syncCargando[id]) return;
this.syncCargando[id] = true;
try {
const r = await fetch(`/app/servidor/${id}/sync-hostinger`, { method: 'POST' });
const data = await r.json();
if (data.ok) {
await this.cargarServidores();
} else {
alert('Error: ' + (data.error || 'No se pudo sincronizar'));
}
} catch (e) {
alert('Error al sincronizar con Hostinger: ' + e.message);
} finally {
this.syncCargando[id] = false;
}
},
agentOnline(servidor) {
if (!servidor?.agent_last_seen) return false;
const last = new Date(servidor.agent_last_seen);