diff --git a/pkg/models/conx_db.go b/pkg/models/conx_db.go
index 9acd1cc..bf01e27 100755
--- a/pkg/models/conx_db.go
+++ b/pkg/models/conx_db.go
@@ -10,6 +10,7 @@ import (
type ConxDb struct {
gorm.Model
Nombre string `json:"nombre" gorm:"column:nombre"`
+ Host string `json:"host" gorm:"column:host"` // vacío = usa Servidor.IpServidor
Puerto string `json:"puerto" gorm:"column:puerto"`
Usuario string `json:"usuario" gorm:"column:usuario"`
Password string `json:"password" gorm:"column:password"`
@@ -21,6 +22,15 @@ type ConxDb struct {
Servidor Servidor `json:"servidor" gorm:"foreignKey:ServidorID"`
}
+// HostEfectivo devuelve el host de conexión: el campo Host si está definido,
+// o el IP del servidor asociado como fallback.
+func (c ConxDb) HostEfectivo() string {
+ if c.Host != "" {
+ return c.Host
+ }
+ return c.Servidor.IpServidor
+}
+
func (ConxDb) TableName() string {
return "conx_db"
}
diff --git a/pkg/services/query_runner_service.go b/pkg/services/query_runner_service.go
index 126ce4a..e29b0d6 100644
--- a/pkg/services/query_runner_service.go
+++ b/pkg/services/query_runner_service.go
@@ -39,7 +39,7 @@ type QueryResult struct {
// openDynamicDB abre una conexión a la base de datos indicada por ConxDb.
func openDynamicDB(c models.ConxDb) (*sql.DB, error) {
driver := strings.ToLower(c.TipoDb.Nombre)
- host := c.Servidor.IpServidor
+ host := c.HostEfectivo()
port := c.Puerto
user := c.Usuario
pass := c.Password
@@ -319,7 +319,7 @@ func TestDBConnection(conx models.ConxDb) error {
// ── helpers ──────────────────────────────────────────────────────────────────
func openDynamicDBWithName(c models.ConxDb, dbName string) (*sql.DB, error) {
- host := c.Servidor.IpServidor
+ host := c.HostEfectivo()
port := c.Puerto
user := c.Usuario
pass := c.Password
@@ -486,7 +486,7 @@ func isRedisDriver(driver string) bool {
}
func redisConnect(c models.ConxDb, dbIndex int) (*goredis.Client, error) {
- host := c.Servidor.IpServidor
+ host := c.HostEfectivo()
port := c.Puerto
pass := c.Password
if port == "" {
@@ -637,10 +637,10 @@ func redisExecuteCommand(conx models.ConxDb, database, cmdText string) QueryResu
}
elapsed := time.Since(start).Milliseconds()
result := QueryResult{
- Columns: []string{"command", "result", "error"},
- Rows: rows,
- RowCount: len(rows),
- IsSelect: true,
+ Columns: []string{"command", "result", "error"},
+ Rows: rows,
+ RowCount: len(rows),
+ IsSelect: true,
DurationMs: elapsed,
}
saveHistory(conx.ID, cmdText, "ok", "", int64(len(rows)), elapsed)
@@ -700,11 +700,11 @@ func redisResultToQueryResult(val any, cmd string) QueryResult {
label = "count"
}
return QueryResult{
- IsSelect: false,
+ IsSelect: false,
AffectedRows: v,
- Columns: []string{label},
- Rows: []map[string]any{{label: v}},
- RowCount: 1,
+ Columns: []string{label},
+ Rows: []map[string]any{{label: v}},
+ RowCount: 1,
}
case []any:
// Lista o conjunto de valores
@@ -797,7 +797,7 @@ func isMongoDriver(driver string) bool {
}
func mongoURI(c models.ConxDb) string {
- host := c.Servidor.IpServidor
+ host := c.HostEfectivo()
port := c.Puerto
user := c.Usuario
pass := c.Password
diff --git a/resources/views/servidor_dashboard.html b/resources/views/servidor_dashboard.html
index de521c3..a66ea08 100644
--- a/resources/views/servidor_dashboard.html
+++ b/resources/views/servidor_dashboard.html
@@ -288,10 +288,16 @@
-
-
-
+ class="rounded-lg px-3 py-2 border text-xs flex flex-col gap-1">
+
+
+
+ Sin conexión
+
+
+
@@ -575,8 +581,10 @@
async hacerPing(cx) {
const id = cx.ID || cx.id;
if (!id) return;
- this.pingCargando[id] = true;
- this.pingResultado[id] = undefined;
+ // Spread-replace para forzar reactividad Alpine v3 con keys nuevas
+ this.pingCargando = { ...this.pingCargando, [id]: true };
+ this.pingResultado = { ...this.pingResultado, [id]: null };
+ await this.$nextTick();
try {
const r = await fetch(`/app/conx-ping/${id}`);
if (!r.ok) {
@@ -584,11 +592,11 @@
throw new Error(`HTTP ${r.status}${txt ? ': ' + txt.substring(0, 80) : ''}`);
}
const data = await r.json();
- this.pingResultado[id] = data;
+ this.pingResultado = { ...this.pingResultado, [id]: data };
} catch (e) {
- this.pingResultado[id] = { exitoso: false, error: e.message || 'Sin conexión' };
+ this.pingResultado = { ...this.pingResultado, [id]: { exitoso: false, error: e.message || 'Sin conexión' } };
} finally {
- this.pingCargando[id] = false;
+ this.pingCargando = { ...this.pingCargando, [id]: false };
}
},
diff --git a/rest/controllers/servidor_controller.go b/rest/controllers/servidor_controller.go
index 551419f..43383da 100755
--- a/rest/controllers/servidor_controller.go
+++ b/rest/controllers/servidor_controller.go
@@ -235,7 +235,6 @@ func PingConexion(c *fiber.Ctx) error {
})
}
- // Obtener conexión de BD
var conexion models.ConxDb
db := app.Http.Database.DB
if err := db.Preload("TipoDb").Preload("Servidor").First(&conexion, uid).Error; err != nil {
@@ -245,41 +244,46 @@ func PingConexion(c *fiber.Ctx) error {
})
}
- // Realizar ping a la conexión
+ host := conexion.HostEfectivo()
+ puerto := conexion.Puerto
+
+ // Primero intentar conexión real a la BD (más fiable que solo TCP)
inicio := time.Now()
- exitoso := false
- var tiempoMs int64
+ 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": "",
+ })
+ }
- // Intentar conexión TCP
- addr := fmt.Sprintf("%s:%s", conexion.Servidor.IpServidor, conexion.Puerto)
- conn, err := net.DialTimeout("tcp", addr, 2*time.Second)
- if err == nil {
+ // 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()
- exitoso = true
- tiempoMs = time.Since(inicio).Milliseconds()
- }
-
- // Si falla TCP, intentar con la BD específica
- if !exitoso {
- inicio = time.Now()
- testErr := services.TestDBConnection(conexion)
- if testErr == nil {
- exitoso = true
- tiempoMs = time.Since(inicio).Milliseconds()
- }
- }
-
- errorMsg := ""
- if !exitoso {
- errorMsg = fmt.Sprintf("No se pudo conectar a %s:%s", conexion.Servidor.IpServidor, conexion.Puerto)
+ 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": exitoso,
- "tiempo": tiempoMs,
- "host": conexion.Servidor.IpServidor,
- "puerto": conexion.Puerto,
+ "exitoso": false,
+ "tiempo": int64(0),
+ "host": host,
+ "puerto": puerto,
"tipo": conexion.TipoDb.Nombre,
- "error": errorMsg,
+ "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),
})
}