fix: query shared MariaDB for node states instead of local DB

In shared mode, the master may use SQLite locally while workers
write heartbeats to the shared MariaDB. The /list endpoint now
opens a temporary MariaDB connection to query node_states when
the local DB is not MariaDB.
This commit is contained in:
root 2026-04-24 18:01:59 +08:00
parent c1a9831e85
commit d5bf2858ce
3 changed files with 35 additions and 2 deletions

View file

@ -1 +1 @@
v1.6.1
v1.6.2

View file

@ -266,6 +266,15 @@ func buildMariaDBDSN(dbConfig config.DBConfig) string {
return cfg.FormatDSN()
}
// OpenMariaDB opens a new MariaDB connection from the given config.
// Caller must close the returned db when done.
func OpenMariaDB(dbConfig config.DBConfig) (*gorm.DB, error) {
dsn := buildMariaDBDSN(dbConfig)
return gorm.Open(mysql.Open(dsn), &gorm.Config{
Logger: logger.Discard,
})
}
// initMariaDB opens a MariaDB connection and runs model migrations.
func initMariaDB() error {
dbConfig := config.GetDBConfigFromJSON()

View file

@ -7,6 +7,7 @@ import (
"github.com/mhsanaei/3x-ui/v2/config"
"github.com/mhsanaei/3x-ui/v2/database"
"github.com/mhsanaei/3x-ui/v2/database/model"
"github.com/gin-gonic/gin"
)
@ -41,10 +42,33 @@ type NodeView struct {
LastError string `json:"lastError"`
}
// getNodeStatesFromShared queries node_states from the shared MariaDB.
// In shared mode, the master may use SQLite locally, so we must query
// the shared MariaDB directly to see worker heartbeats.
func getNodeStatesFromShared() ([]model.NodeState, error) {
// If current DB is already MariaDB, use it directly
if config.GetDBTypeFromJSON() == "mariadb" {
return database.GetNodeStates()
}
// Otherwise, open a temporary connection to the shared MariaDB
dbConfig := config.GetDBConfigFromJSON()
db, err := database.OpenMariaDB(dbConfig)
if err != nil {
return nil, err
}
sqlDB, _ := db.DB()
defer sqlDB.Close()
var states []model.NodeState
err = db.Order("node_id").Find(&states).Error
return states, err
}
// list returns connected nodes. Master sees all workers; worker sees the master.
func (a *NodeController) list(c *gin.Context) {
nodeCfg := config.GetNodeConfigFromJSON()
states, err := database.GetNodeStates()
states, err := getNodeStatesFromShared()
if err != nil {
jsonMsg(c, "get node states", err)
return