Problema raíz:
1. El ping TCP usaba Servidor.IpServidor → fallaba si la BD solo escucha
en localhost del servidor remoto (comportamiento normal en producción)
2. La reactividad Alpine v3.13 no detectaba keys nuevas en objeto vacío {}
dentro de x-for loops, spinner nunca aparecía
Cambios:
- pkg/models/conx_db.go: nuevo campo Host (vacío = usa Servidor.IpServidor)
+ método HostEfectivo() como fuente única de verdad para host
- pkg/services/query_runner_service.go: todos los puntos (openDynamicDB,
openDynamicDBWithName, redisConnect, mongoURI) usan c.HostEfectivo()
- rest/controllers/servidor_controller.go: PingConexion reescrito —
primero prueba conexión real a la BD (no solo TCP), si falla prueba TCP,
devuelve host+puerto testeado y mensajes de error descriptivos
- servidor_dashboard.html: hacerPing usa spread-replace para forzar
reactividad + $nextTick, resultado muestra host:puerto testeado y
mensaje de error completo en múltiples líneas
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
290 lines
7.6 KiB
Go
Executable File
290 lines
7.6 KiB
Go
Executable File
package controllers
|
|
|
|
import (
|
|
"fmt"
|
|
"math"
|
|
"net"
|
|
"strconv"
|
|
"time"
|
|
|
|
"github.com/gofiber/fiber/v2"
|
|
"github.com/sujit-baniya/fiber-boilerplate/app"
|
|
"github.com/sujit-baniya/fiber-boilerplate/pkg/models"
|
|
"github.com/sujit-baniya/fiber-boilerplate/pkg/services"
|
|
)
|
|
|
|
// RENDER
|
|
func Servidor(c *fiber.Ctx) error {
|
|
data := fiber.Map{
|
|
"user": c.Locals("user").(map[string]interface{}),
|
|
"modules": c.Locals("userModules"),
|
|
}
|
|
|
|
if err := c.Render("servidor", data, "layouts/main"); err != nil {
|
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
|
|
"message": "Error rendering template",
|
|
"error": err.Error(),
|
|
})
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// ServidorDashboard renders the server dashboard view
|
|
func ServidorDashboard(c *fiber.Ctx) error {
|
|
data := fiber.Map{
|
|
"user": c.Locals("user").(map[string]interface{}),
|
|
"modules": c.Locals("userModules"),
|
|
}
|
|
|
|
if err := c.Render("servidor_dashboard", data, "layouts/main"); err != nil {
|
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
|
|
"message": "Error rendering template",
|
|
"error": err.Error(),
|
|
})
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func GetServidor(c *fiber.Ctx) error {
|
|
pageStr := c.Query("page", "1")
|
|
page, err := strconv.Atoi(pageStr)
|
|
if err != nil || page < 1 {
|
|
page = 1
|
|
}
|
|
searchQuery := c.Query("search", "")
|
|
limitStr := c.Query("limit", "10")
|
|
limit, _ := strconv.Atoi(limitStr)
|
|
offset := (page - 1) * limit
|
|
|
|
records, total, err := models.GetAllServidores(limit, offset, searchQuery)
|
|
if err != nil {
|
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
|
|
"message": "Error retrieving records",
|
|
"error": err.Error(),
|
|
})
|
|
}
|
|
|
|
// Build connection count map per server
|
|
type conxCount struct {
|
|
ServidorID uint `gorm:"column:servidor_id"`
|
|
Count int64 `gorm:"column:count"`
|
|
}
|
|
var counts []conxCount
|
|
serverIDs := make([]uint, len(records))
|
|
for i, s := range records {
|
|
serverIDs[i] = s.ID
|
|
}
|
|
if len(serverIDs) > 0 {
|
|
app.Http.Database.DB.Table("conx_db").
|
|
Select("servidor_id, count(*) as count").
|
|
Where("servidor_id IN ? AND deleted_at IS NULL", serverIDs).
|
|
Group("servidor_id").
|
|
Scan(&counts)
|
|
}
|
|
countMap := make(map[uint]int64)
|
|
for _, cc := range counts {
|
|
countMap[cc.ServidorID] = cc.Count
|
|
}
|
|
|
|
// Attach conx_count to each record
|
|
type servidorWithCount struct {
|
|
models.Servidor
|
|
ConxCount int64 `json:"conx_count"`
|
|
}
|
|
enriched := make([]servidorWithCount, len(records))
|
|
for i, s := range records {
|
|
enriched[i] = servidorWithCount{Servidor: s, ConxCount: countMap[s.ID]}
|
|
}
|
|
|
|
totalPages := int(math.Ceil(float64(total) / float64(limit)))
|
|
return c.JSON(fiber.Map{
|
|
"registros": enriched,
|
|
"total": total,
|
|
"totalPages": totalPages,
|
|
"page": page,
|
|
"limit": limit,
|
|
})
|
|
}
|
|
|
|
func GetServidorSelect(c *fiber.Ctx) error {
|
|
records, err := models.GetAllServidoresSelect()
|
|
if err != nil {
|
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
|
|
"message": "Error retrieving records",
|
|
"error": err.Error(),
|
|
})
|
|
}
|
|
return c.JSON(fiber.Map{
|
|
"registros": records,
|
|
})
|
|
}
|
|
|
|
func CreateServidor(c *fiber.Ctx) error {
|
|
var m models.Servidor
|
|
|
|
if err := c.BodyParser(&m); err != nil {
|
|
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
|
|
"message": "Error parsing request body",
|
|
"error": err.Error(),
|
|
})
|
|
}
|
|
|
|
if err := models.CreateServidor(m); err != nil {
|
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
|
|
"message": "Error creating servidor",
|
|
"error": err.Error(),
|
|
})
|
|
}
|
|
|
|
return c.Status(fiber.StatusCreated).JSON(m)
|
|
}
|
|
|
|
func UpdateServidor(c *fiber.Ctx) error {
|
|
uid, err := strconv.ParseUint(c.Params("id"), 10, 32)
|
|
if err != nil {
|
|
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
|
|
"message": "ID inválido",
|
|
"error": err.Error(),
|
|
})
|
|
}
|
|
|
|
var m models.Servidor
|
|
|
|
if err := c.BodyParser(&m); err != nil {
|
|
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
|
|
"message": "Error al parsear el cuerpo de la solicitud",
|
|
"error": err.Error(),
|
|
})
|
|
}
|
|
|
|
m.ID = uint(uid)
|
|
|
|
if err := models.UpdateServidor(m); err != nil {
|
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
|
|
"message": "Error al actualizar el servidor",
|
|
"error": err.Error(),
|
|
})
|
|
}
|
|
|
|
return c.Status(fiber.StatusOK).JSON(m)
|
|
}
|
|
|
|
func DeleteServidor(c *fiber.Ctx) error {
|
|
uid, err := strconv.ParseUint(c.Params("id"), 10, 32)
|
|
if err != nil {
|
|
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
|
|
"message": "ID inválido",
|
|
"error": err.Error(),
|
|
})
|
|
}
|
|
|
|
var m models.Servidor
|
|
m.ID = uint(uid)
|
|
|
|
if err := models.DeleteServidor(m); err != nil {
|
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
|
|
"message": "Error al eliminar el servidor",
|
|
"error": err.Error(),
|
|
})
|
|
}
|
|
|
|
return c.Status(fiber.StatusOK).JSON(fiber.Map{
|
|
"message": "Servidor eliminado exitosamente",
|
|
})
|
|
}
|
|
|
|
// GetServidorDashboard retorna un servidor con todas sus conexiones de BD
|
|
func GetServidorDashboard(c *fiber.Ctx) error {
|
|
servidorID := c.Params("id")
|
|
uid, err := strconv.ParseUint(servidorID, 10, 32)
|
|
if err != nil {
|
|
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
|
|
"error": "ID inválido",
|
|
})
|
|
}
|
|
|
|
// Obtener servidor
|
|
var servidor models.Servidor
|
|
db := app.Http.Database.DB
|
|
if err := db.Preload("ProvServidor").Preload("TipoServidor").First(&servidor, uid).Error; err != nil {
|
|
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{
|
|
"error": "Servidor no encontrado",
|
|
})
|
|
}
|
|
|
|
// Obtener conexiones de BD asociadas
|
|
var conexiones []models.ConxDb
|
|
if err := db.Preload("Servidor").Preload("TipoDb").Where("servidor_id = ?", uid).Find(&conexiones).Error; err != nil {
|
|
conexiones = []models.ConxDb{}
|
|
}
|
|
|
|
return c.JSON(fiber.Map{
|
|
"servidor": servidor,
|
|
"conexiones": conexiones,
|
|
})
|
|
}
|
|
|
|
// PingConexion tests the connection to a database and returns the response time
|
|
func PingConexion(c *fiber.Ctx) error {
|
|
conexionID := c.Params("id")
|
|
uid, err := strconv.ParseUint(conexionID, 10, 32)
|
|
if err != nil {
|
|
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
|
|
"exitoso": false,
|
|
"error": "ID inválido",
|
|
})
|
|
}
|
|
|
|
var conexion models.ConxDb
|
|
db := app.Http.Database.DB
|
|
if err := db.Preload("TipoDb").Preload("Servidor").First(&conexion, uid).Error; err != nil {
|
|
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{
|
|
"exitoso": false,
|
|
"error": "Conexión no encontrada",
|
|
})
|
|
}
|
|
|
|
host := conexion.HostEfectivo()
|
|
puerto := conexion.Puerto
|
|
|
|
// Primero intentar conexión real a la BD (más fiable que solo TCP)
|
|
inicio := time.Now()
|
|
testErr := services.TestDBConnection(conexion)
|
|
if testErr == nil {
|
|
return c.JSON(fiber.Map{
|
|
"exitoso": true,
|
|
"tiempo": time.Since(inicio).Milliseconds(),
|
|
"host": host,
|
|
"puerto": puerto,
|
|
"tipo": conexion.TipoDb.Nombre,
|
|
"error": "",
|
|
})
|
|
}
|
|
|
|
// Si la BD no conecta, probar al menos TCP (puerto abierto)
|
|
tcpAddr := fmt.Sprintf("%s:%s", host, puerto)
|
|
inicioTCP := time.Now()
|
|
conn, errTCP := net.DialTimeout("tcp", tcpAddr, 3*time.Second)
|
|
if errTCP == nil {
|
|
conn.Close()
|
|
return c.JSON(fiber.Map{
|
|
"exitoso": false,
|
|
"tiempo": time.Since(inicioTCP).Milliseconds(),
|
|
"host": host,
|
|
"puerto": puerto,
|
|
"tipo": conexion.TipoDb.Nombre,
|
|
// Puerto abierto pero la BD rechaza la conexión
|
|
"error": fmt.Sprintf("Puerto %s abierto pero la BD no responde: %s", puerto, testErr.Error()),
|
|
})
|
|
}
|
|
|
|
return c.JSON(fiber.Map{
|
|
"exitoso": false,
|
|
"tiempo": int64(0),
|
|
"host": host,
|
|
"puerto": puerto,
|
|
"tipo": conexion.TipoDb.Nombre,
|
|
"error": fmt.Sprintf("No se puede alcanzar %s:%s — verifica que el host sea accesible desde este servidor. Tip: si el DB solo escucha en localhost del servidor, configura el campo 'Host' de la conexión.", host, puerto),
|
|
})
|
|
}
|