mirror of
https://github.com/0xJacky/nginx-ui.git
synced 2026-05-22 11:27:34 +08:00
164 lines
4.0 KiB
Go
164 lines
4.0 KiB
Go
package sites
|
|
|
|
import (
|
|
"net/http"
|
|
"sync"
|
|
|
|
"github.com/0xJacky/Nginx-UI/internal/helper"
|
|
"github.com/0xJacky/Nginx-UI/internal/sitecheck"
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/gorilla/websocket"
|
|
"github.com/uozi-tech/cosy/logger"
|
|
)
|
|
|
|
// WebSocket message types
|
|
const (
|
|
MessageTypeInitial = "initial"
|
|
MessageTypeUpdate = "update"
|
|
MessageTypeRefresh = "refresh"
|
|
MessageTypePing = "ping"
|
|
MessageTypePong = "pong"
|
|
)
|
|
|
|
// ClientMessage represents incoming WebSocket messages from client
|
|
type ClientMessage struct {
|
|
Type string `json:"type"`
|
|
}
|
|
|
|
// ServerMessage represents outgoing WebSocket messages to client
|
|
type ServerMessage struct {
|
|
Type string `json:"type"`
|
|
Data []*sitecheck.SiteInfo `json:"data,omitempty"`
|
|
}
|
|
|
|
// PongMessage represents a pong response
|
|
type PongMessage struct {
|
|
Type string `json:"type"`
|
|
}
|
|
|
|
var upgrader = websocket.Upgrader{
|
|
CheckOrigin: func(r *http.Request) bool {
|
|
return true
|
|
},
|
|
}
|
|
|
|
// WSManager WebSocket connection manager
|
|
type WSManager struct {
|
|
connections map[*websocket.Conn]bool
|
|
mutex sync.RWMutex
|
|
}
|
|
|
|
var wsManager = &WSManager{
|
|
connections: make(map[*websocket.Conn]bool),
|
|
}
|
|
|
|
// AddConnection adds a WebSocket connection to the manager
|
|
func (wm *WSManager) AddConnection(conn *websocket.Conn) {
|
|
wm.mutex.Lock()
|
|
defer wm.mutex.Unlock()
|
|
wm.connections[conn] = true
|
|
}
|
|
|
|
// RemoveConnection removes a WebSocket connection from the manager
|
|
func (wm *WSManager) RemoveConnection(conn *websocket.Conn) {
|
|
wm.mutex.Lock()
|
|
defer wm.mutex.Unlock()
|
|
delete(wm.connections, conn)
|
|
}
|
|
|
|
// BroadcastUpdate sends updates to all connected WebSocket clients
|
|
func (wm *WSManager) BroadcastUpdate(sites []*sitecheck.SiteInfo) {
|
|
wm.mutex.RLock()
|
|
defer wm.mutex.RUnlock()
|
|
|
|
for conn := range wm.connections {
|
|
go func(c *websocket.Conn) {
|
|
if err := sendSiteData(c, MessageTypeUpdate, sites); err != nil {
|
|
wm.RemoveConnection(c)
|
|
c.Close()
|
|
}
|
|
}(conn)
|
|
}
|
|
}
|
|
|
|
// GetManager returns the global WebSocket manager instance
|
|
func GetManager() *WSManager {
|
|
return wsManager
|
|
}
|
|
|
|
// InitWebSocketNotifications sets up the callback for site check updates
|
|
func InitWebSocketNotifications() {
|
|
service := sitecheck.GetService()
|
|
service.SetUpdateCallback(func(sites []*sitecheck.SiteInfo) {
|
|
wsManager.BroadcastUpdate(sites)
|
|
})
|
|
}
|
|
|
|
// SiteNavigationWebSocket handles WebSocket connections for real-time site status updates
|
|
func SiteNavigationWebSocket(c *gin.Context) {
|
|
ctx := c.Request.Context()
|
|
|
|
conn, err := upgrader.Upgrade(c.Writer, c.Request, nil)
|
|
if err != nil {
|
|
logger.Error("WebSocket upgrade failed:", err)
|
|
return
|
|
}
|
|
defer func() {
|
|
wsManager.RemoveConnection(conn)
|
|
conn.Close()
|
|
}()
|
|
|
|
logger.Info("Site navigation WebSocket connection established")
|
|
|
|
// Register connection with manager
|
|
wsManager.AddConnection(conn)
|
|
|
|
service := sitecheck.GetService()
|
|
|
|
// Send initial data
|
|
if err := sendSiteData(conn, MessageTypeInitial, service.GetSites()); err != nil {
|
|
logger.Error("Failed to send initial data:", err)
|
|
return
|
|
}
|
|
|
|
// Handle incoming messages from client
|
|
go handleClientMessages(conn, service)
|
|
|
|
<-ctx.Done()
|
|
logger.Info("Request context cancelled, closing WebSocket")
|
|
}
|
|
|
|
// sendSiteData sends site data via WebSocket
|
|
func sendSiteData(conn *websocket.Conn, msgType string, sites []*sitecheck.SiteInfo) error {
|
|
message := ServerMessage{
|
|
Type: msgType,
|
|
Data: sites,
|
|
}
|
|
return conn.WriteJSON(message)
|
|
}
|
|
|
|
// handleClientMessages handles incoming WebSocket messages
|
|
func handleClientMessages(conn *websocket.Conn, service *sitecheck.Service) {
|
|
for {
|
|
var msg ClientMessage
|
|
if err := conn.ReadJSON(&msg); err != nil {
|
|
if helper.IsUnexpectedWebsocketError(err) {
|
|
logger.Error("WebSocket read error:", err)
|
|
}
|
|
return
|
|
}
|
|
|
|
switch msg.Type {
|
|
case MessageTypeRefresh:
|
|
logger.Info("Client requested site refresh")
|
|
service.RefreshSites()
|
|
case MessageTypePing:
|
|
pongMsg := PongMessage{Type: MessageTypePong}
|
|
if err := conn.WriteJSON(pongMsg); err != nil {
|
|
logger.Error("Failed to send pong:", err)
|
|
return
|
|
}
|
|
}
|
|
}
|
|
}
|