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
+34
View File
@@ -3,6 +3,8 @@ package controllers
import (
"crypto/rand"
"encoding/hex"
"encoding/json"
"fmt"
"time"
"github.com/gofiber/fiber/v2"
@@ -35,9 +37,41 @@ func AgentHeartbeat(c *fiber.Ctx) error {
now := time.Now()
updates := map[string]interface{}{
"agent_last_seen": &now,
"ultimo_ping": now.Format(time.RFC3339),
}
if 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 {
+83
View File
@@ -5,6 +5,7 @@ import (
"math"
"net"
"strconv"
"strings"
"time"
"github.com/gofiber/fiber/v2"
@@ -159,6 +160,11 @@ func UpdateServidor(c *fiber.Ctx) error {
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 {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
"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)
}
// 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 {
uid, err := strconv.ParseUint(c.Params("id"), 10, 32)
if err != nil {