up
This commit is contained in:
@@ -26,6 +26,9 @@ type Servidor struct {
|
|||||||
AgentToken string `json:"agent_token" gorm:"column:agent_token;uniqueIndex"`
|
AgentToken string `json:"agent_token" gorm:"column:agent_token;uniqueIndex"`
|
||||||
AgentLastSeen *time.Time `json:"agent_last_seen" gorm:"column:agent_last_seen"`
|
AgentLastSeen *time.Time `json:"agent_last_seen" gorm:"column:agent_last_seen"`
|
||||||
MetricasJson string `json:"metricas_json" gorm:"column:metricas_json;type:text"`
|
MetricasJson string `json:"metricas_json" gorm:"column:metricas_json;type:text"`
|
||||||
|
// Integración Hostinger
|
||||||
|
HostingerVpsID *int `json:"hostinger_vps_id" gorm:"column:hostinger_vps_id"`
|
||||||
|
HostingerState string `json:"hostinger_state" gorm:"column:hostinger_state"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (Servidor) TableName() string {
|
func (Servidor) TableName() string {
|
||||||
|
|||||||
@@ -191,6 +191,20 @@ type HostingerHosting struct {
|
|||||||
|
|
||||||
// ─── Métodos de la API ───────────────────────────────────────────────────────
|
// ─── Métodos de la API ───────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
// GetVPSByID obtiene una VM específica por su ID.
|
||||||
|
func (c *HostingerClient) GetVPSByID(vmID int) (*HostingerVPS, error) {
|
||||||
|
path := fmt.Sprintf("/vps/v1/virtual-machines/%d", vmID)
|
||||||
|
body, err := c.getRaw(context.Background(), path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var vps HostingerVPS
|
||||||
|
if err := json.Unmarshal(body, &vps); err != nil {
|
||||||
|
return nil, fmt.Errorf("hostinger: no se pudo interpretar respuesta VPS: %w", err)
|
||||||
|
}
|
||||||
|
return &vps, nil
|
||||||
|
}
|
||||||
|
|
||||||
// GetVPSList obtiene la lista de VPS/VMs.
|
// GetVPSList obtiene la lista de VPS/VMs.
|
||||||
func (c *HostingerClient) GetVPSList() ([]HostingerVPS, error) {
|
func (c *HostingerClient) GetVPSList() ([]HostingerVPS, error) {
|
||||||
body, err := c.getRaw(context.Background(), "/vps/v1/virtual-machines")
|
body, err := c.getRaw(context.Background(), "/vps/v1/virtual-machines")
|
||||||
|
|||||||
@@ -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" />
|
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>
|
</svg>
|
||||||
</button>
|
</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>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -262,6 +269,21 @@
|
|||||||
</div>
|
</div>
|
||||||
</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">
|
<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="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>
|
<button @click="closeModals()" class="px-4 py-2 bg-gray-600 text-white rounded">Cerrar</button>
|
||||||
@@ -344,6 +366,14 @@
|
|||||||
</div>
|
</div>
|
||||||
</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">
|
<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="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>
|
<button @click="addModal = false" class="px-4 py-2 bg-gray-600 text-white rounded">Cancelar</button>
|
||||||
@@ -386,6 +416,7 @@
|
|||||||
deleteModal: false,
|
deleteModal: false,
|
||||||
selectedItem: {},
|
selectedItem: {},
|
||||||
errors: {},
|
errors: {},
|
||||||
|
syncCargando: {},
|
||||||
init() {
|
init() {
|
||||||
this.loadData();
|
this.loadData();
|
||||||
this.loadProvServidor();
|
this.loadProvServidor();
|
||||||
@@ -421,6 +452,8 @@
|
|||||||
ultimo_ping: data.ultimo_ping,
|
ultimo_ping: data.ultimo_ping,
|
||||||
prov_servidor_id: data.prov_servidor_id,
|
prov_servidor_id: data.prov_servidor_id,
|
||||||
tipo_servidor_id: data.tipo_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,
|
created_at: data.CreatedAt,
|
||||||
}));
|
}));
|
||||||
console.log('📊 Servidores procesados:', this.datos.length);
|
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() {
|
updateItem() {
|
||||||
if (this.selectedItem.prov_servidor_id) {
|
if (this.selectedItem.prov_servidor_id) {
|
||||||
this.selectedItem.prov_servidor_id = parseInt(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,
|
ultimo_ping: this.selectedItem.ultimo_ping,
|
||||||
prov_servidor_id: this.selectedItem.prov_servidor_id,
|
prov_servidor_id: this.selectedItem.prov_servidor_id,
|
||||||
tipo_servidor_id: this.selectedItem.tipo_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);
|
this.setLoading(true);
|
||||||
|
|||||||
@@ -46,8 +46,11 @@
|
|||||||
<span x-show="agentOnline(servidor)">● En línea</span>
|
<span x-show="agentOnline(servidor)">● En línea</span>
|
||||||
<span x-show="!agentOnline(servidor) && servidor.agent_token">● Fuera</span>
|
<span x-show="!agentOnline(servidor) && servidor.agent_token">● Fuera</span>
|
||||||
<span x-show="!servidor.agent_token">○ Sin agente</span>
|
<span x-show="!servidor.agent_token">○ Sin agente</span>
|
||||||
</span>
|
</span> <!-- Badge estado Hostinger -->
|
||||||
</div>
|
<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>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -153,6 +156,14 @@
|
|||||||
</svg>
|
</svg>
|
||||||
Agente
|
Agente
|
||||||
</button>
|
</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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -484,6 +495,7 @@
|
|||||||
agentTokenCargando: false,
|
agentTokenCargando: false,
|
||||||
cargando: true,
|
cargando: true,
|
||||||
cargandoConexiones: false,
|
cargandoConexiones: false,
|
||||||
|
syncCargando: {},
|
||||||
pingCargando: {},
|
pingCargando: {},
|
||||||
pingResultado: {},
|
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) {
|
agentOnline(servidor) {
|
||||||
if (!servidor?.agent_last_seen) return false;
|
if (!servidor?.agent_last_seen) return false;
|
||||||
const last = new Date(servidor.agent_last_seen);
|
const last = new Date(servidor.agent_last_seen);
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ package controllers
|
|||||||
import (
|
import (
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
@@ -35,9 +37,41 @@ func AgentHeartbeat(c *fiber.Ctx) error {
|
|||||||
now := time.Now()
|
now := time.Now()
|
||||||
updates := map[string]interface{}{
|
updates := map[string]interface{}{
|
||||||
"agent_last_seen": &now,
|
"agent_last_seen": &now,
|
||||||
|
"ultimo_ping": now.Format(time.RFC3339),
|
||||||
}
|
}
|
||||||
if req.Metricas != "" {
|
if req.Metricas != "" {
|
||||||
updates["metricas_json"] = req.Metricas
|
updates["metricas_json"] = req.Metricas
|
||||||
|
// Actualizar campos planos desde las métricas del agente (la verdad viene del agente)
|
||||||
|
type agentRAM struct {
|
||||||
|
TotalGB float64 `json:"total_gb"`
|
||||||
|
}
|
||||||
|
type agentCPU struct {
|
||||||
|
Nucleos int `json:"nucleos"`
|
||||||
|
}
|
||||||
|
type agentDisco struct {
|
||||||
|
TotalGB float64 `json:"total_gb"`
|
||||||
|
}
|
||||||
|
type agentPayload struct {
|
||||||
|
OS string `json:"os"`
|
||||||
|
RAM agentRAM `json:"ram"`
|
||||||
|
CPU agentCPU `json:"cpu"`
|
||||||
|
Disco agentDisco `json:"disco"`
|
||||||
|
}
|
||||||
|
var m agentPayload
|
||||||
|
if jsonErr := json.Unmarshal([]byte(req.Metricas), &m); jsonErr == nil {
|
||||||
|
if m.OS != "" {
|
||||||
|
updates["so"] = m.OS
|
||||||
|
}
|
||||||
|
if m.RAM.TotalGB > 0 {
|
||||||
|
updates["ram"] = fmt.Sprintf("%.0f GB", m.RAM.TotalGB)
|
||||||
|
}
|
||||||
|
if m.CPU.Nucleos > 0 {
|
||||||
|
updates["nucleos"] = fmt.Sprintf("%d", m.CPU.Nucleos)
|
||||||
|
}
|
||||||
|
if m.Disco.TotalGB > 0 {
|
||||||
|
updates["disco"] = fmt.Sprintf("%.0f GB", m.Disco.TotalGB)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := db.Model(&servidor).Updates(updates).Error; err != nil {
|
if err := db.Model(&servidor).Updates(updates).Error; err != nil {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"math"
|
"math"
|
||||||
"net"
|
"net"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
@@ -159,6 +160,11 @@ func UpdateServidor(c *fiber.Ctx) error {
|
|||||||
|
|
||||||
m.ID = uint(uid)
|
m.ID = uint(uid)
|
||||||
|
|
||||||
|
// Cargar el registro actual para detectar cambios que deben propagarse a Hostinger
|
||||||
|
var actual models.Servidor
|
||||||
|
app.Http.Database.DB.First(&actual, uid)
|
||||||
|
nombreAnterior := actual.Nombre
|
||||||
|
|
||||||
if err := models.UpdateServidor(m); err != nil {
|
if err := models.UpdateServidor(m); err != nil {
|
||||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
|
||||||
"message": "Error al actualizar el servidor",
|
"message": "Error al actualizar el servidor",
|
||||||
@@ -166,9 +172,86 @@ func UpdateServidor(c *fiber.Ctx) error {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sincronizar hostname en Hostinger si está vinculado y el nombre cambió
|
||||||
|
if actual.HostingerVpsID != nil && m.Nombre != "" && m.Nombre != nombreAnterior {
|
||||||
|
if client, hErr := hostingerClient(); hErr == nil {
|
||||||
|
_ = client.SetVPSHostname(*actual.HostingerVpsID, m.Nombre)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return c.Status(fiber.StatusOK).JSON(m)
|
return c.Status(fiber.StatusOK).JSON(m)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SyncServidorFromHostinger sincroniza los datos del VPS de Hostinger hacia el servidor local.
|
||||||
|
// Actualiza: ip_servidor, ram, nucleos, disco, hostinger_state, vencimiento (si hay suscripción).
|
||||||
|
// La verdad de las specs físicas siempre viene de Hostinger.
|
||||||
|
func SyncServidorFromHostinger(c *fiber.Ctx) error {
|
||||||
|
id := c.Params("id")
|
||||||
|
db := app.Http.Database.DB
|
||||||
|
|
||||||
|
var servidor models.Servidor
|
||||||
|
if err := db.First(&servidor, id).Error; err != nil {
|
||||||
|
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "servidor no encontrado"})
|
||||||
|
}
|
||||||
|
if servidor.HostingerVpsID == nil {
|
||||||
|
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "el servidor no tiene hostinger_vps_id configurado"})
|
||||||
|
}
|
||||||
|
|
||||||
|
client, err := hostingerClient()
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(fiber.StatusFailedDependency).JSON(fiber.Map{"error": "No se encontró configuración activa de Hostinger"})
|
||||||
|
}
|
||||||
|
|
||||||
|
vps, err := client.GetVPSByID(*servidor.HostingerVpsID)
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(fiber.StatusUnprocessableEntity).JSON(fiber.Map{"error": err.Error()})
|
||||||
|
}
|
||||||
|
|
||||||
|
updates := map[string]interface{}{
|
||||||
|
"hostinger_state": vps.State,
|
||||||
|
}
|
||||||
|
|
||||||
|
// IP principal (primer IPv4)
|
||||||
|
if len(vps.IPV4) > 0 && vps.IPV4[0].Address != "" {
|
||||||
|
updates["ip_servidor"] = vps.IPV4[0].Address
|
||||||
|
}
|
||||||
|
|
||||||
|
// RAM: Hostinger devuelve en MB → convertir a GB
|
||||||
|
if vps.RAMBytes > 0 {
|
||||||
|
gbRam := float64(vps.RAMBytes) / 1024.0
|
||||||
|
updates["ram"] = fmt.Sprintf("%.0f GB", gbRam)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CPU cores
|
||||||
|
if vps.CPU > 0 {
|
||||||
|
updates["nucleos"] = fmt.Sprintf("%d", vps.CPU)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Disco: Hostinger devuelve en GB directamente
|
||||||
|
if vps.DiskBytes > 0 {
|
||||||
|
updates["disco"] = fmt.Sprintf("%d GB", vps.DiskBytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vencimiento: buscar suscripción cuyo nombre contenga el hostname del VPS
|
||||||
|
if subs, sErr := client.GetOrders(); sErr == nil {
|
||||||
|
for _, sub := range subs {
|
||||||
|
if sub.ExpiresAt != "" && len(sub.ExpiresAt) >= 10 &&
|
||||||
|
(strings.Contains(strings.ToLower(sub.Name), strings.ToLower(vps.Hostname)) ||
|
||||||
|
strings.Contains(strings.ToLower(sub.Name), "vps")) {
|
||||||
|
updates["vencimiento"] = sub.ExpiresAt[:10]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := db.Model(&servidor).Updates(updates).Error; err != nil {
|
||||||
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "no se pudo guardar"})
|
||||||
|
}
|
||||||
|
|
||||||
|
db.First(&servidor, id)
|
||||||
|
return c.JSON(fiber.Map{"ok": true, "servidor": servidor})
|
||||||
|
}
|
||||||
|
|
||||||
func DeleteServidor(c *fiber.Ctx) error {
|
func DeleteServidor(c *fiber.Ctx) error {
|
||||||
uid, err := strconv.ParseUint(c.Params("id"), 10, 32)
|
uid, err := strconv.ParseUint(c.Params("id"), 10, 32)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -80,6 +80,7 @@ func UserRoutes(app fiber.Router) {
|
|||||||
protected.Get("/servidor-dashboard/:id", controllers.GetServidorDashboard)
|
protected.Get("/servidor-dashboard/:id", controllers.GetServidorDashboard)
|
||||||
protected.Get("/conx-ping/:id", controllers.PingConexion)
|
protected.Get("/conx-ping/:id", controllers.PingConexion)
|
||||||
protected.Post("/servidor/:id/agent-token", controllers.GenerateAgentToken)
|
protected.Post("/servidor/:id/agent-token", controllers.GenerateAgentToken)
|
||||||
|
protected.Post("/servidor/:id/sync-hostinger", controllers.SyncServidorFromHostinger)
|
||||||
|
|
||||||
// Rutas de proveedores de servidor
|
// Rutas de proveedores de servidor
|
||||||
protected.Get("/prov_servidor", middlewares.MenuMiddleware, controllers.ProvServidor)
|
protected.Get("/prov_servidor", middlewares.MenuMiddleware, controllers.ProvServidor)
|
||||||
|
|||||||
Reference in New Issue
Block a user