package upstream import ( "net/http" "sort" "github.com/0xJacky/Nginx-UI/internal/upstream" "github.com/0xJacky/Nginx-UI/model" "github.com/0xJacky/Nginx-UI/query" "github.com/gin-gonic/gin" "github.com/uozi-tech/cosy" "github.com/uozi-tech/cosy/logger" ) // UpstreamInfo represents an upstream with its configuration and health status type UpstreamInfo struct { Name string `json:"name"` Servers []upstream.ProxyTarget `json:"servers"` ConfigPath string `json:"config_path"` LastSeen string `json:"last_seen"` Status map[string]*upstream.Status `json:"status"` Enabled bool `json:"enabled"` } // GetUpstreamList returns all upstreams with their configuration and health status func GetUpstreamList(c *gin.Context) { service := upstream.GetUpstreamService() // Get all upstream definitions upstreams := service.GetAllUpstreamDefinitions() // Get availability map availabilityMap := service.GetAvailabilityMap() // Get all upstream configurations from database u := query.UpstreamConfig configs, err := u.Find() if err != nil { cosy.ErrHandler(c, err) return } // Create a map for quick lookup of enabled status by upstream name configMap := make(map[string]bool) for _, config := range configs { configMap[config.Socket] = config.Enabled } // Build response result := make([]UpstreamInfo, 0, len(upstreams)) for name, def := range upstreams { // Get enabled status from database, default to true if not found enabled := true if val, exists := configMap[name]; exists { enabled = val } // Get status for each server in this upstream serverStatus := make(map[string]*upstream.Status) for _, server := range def.Servers { key := formatSocketAddress(server.Host, server.Port) if status, exists := availabilityMap[key]; exists { serverStatus[key] = status } } info := UpstreamInfo{ Name: name, Servers: def.Servers, ConfigPath: def.ConfigPath, LastSeen: def.LastSeen.Format("2006-01-02 15:04:05"), Status: serverStatus, Enabled: enabled, } result = append(result, info) } // Sort by name for stable ordering sort.Slice(result, func(i, j int) bool { return result[i].Name < result[j].Name }) c.JSON(http.StatusOK, gin.H{ "data": result, }) } // UpdateUpstreamConfigRequest represents the request body for updating upstream config type UpdateUpstreamConfigRequest struct { Enabled bool `json:"enabled"` } // UpdateUpstreamConfig updates the enabled status of an upstream func UpdateUpstreamConfig(c *gin.Context) { name := c.Param("name") var req UpdateUpstreamConfigRequest if err := c.ShouldBindJSON(&req); err != nil { cosy.ErrHandler(c, err) return } u := query.UpstreamConfig // Check if config exists config, err := u.Where(u.Socket.Eq(name)).First() if err != nil { // Create new config if not found config = &model.UpstreamConfig{ Socket: name, Enabled: req.Enabled, } if err := u.Create(config); err != nil { logger.Error("Failed to create upstream config:", err) cosy.ErrHandler(c, err) return } // Invalidate cache after creating new config service := upstream.GetUpstreamService() service.InvalidateDisabledSocketsCache() } else { // Update existing config if _, err := u.Where(u.Socket.Eq(name)).Update(u.Enabled, req.Enabled); err != nil { logger.Error("Failed to update upstream config:", err) cosy.ErrHandler(c, err) return } } // Invalidate the disabled sockets cache to ensure changes take effect immediately service := upstream.GetUpstreamService() service.InvalidateDisabledSocketsCache() c.JSON(http.StatusOK, gin.H{ "message": "Upstream config updated successfully", }) }