1
0

notifier.go 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. // Package websocket provides WebSocket hub for real-time updates and notifications.
  2. package websocket
  3. import (
  4. "github.com/mhsanaei/3x-ui/v3/internal/logger"
  5. "github.com/mhsanaei/3x-ui/v3/internal/web/global"
  6. )
  7. // GetHub returns the global WebSocket hub instance.
  8. func GetHub() *Hub {
  9. webServer := global.GetWebServer()
  10. if webServer == nil {
  11. return nil
  12. }
  13. hub := webServer.GetWSHub()
  14. if hub == nil {
  15. return nil
  16. }
  17. wsHub, ok := hub.(*Hub)
  18. if !ok {
  19. logger.Warning("WebSocket hub type assertion failed")
  20. return nil
  21. }
  22. return wsHub
  23. }
  24. // HasClients returns true if any WebSocket client is connected.
  25. // Use this to skip expensive work (DB queries, serialization) when no browser is open.
  26. func HasClients() bool {
  27. hub := GetHub()
  28. return hub != nil && hub.GetClientCount() > 0
  29. }
  30. // BroadcastStatus broadcasts server status update to all connected clients.
  31. func BroadcastStatus(status any) {
  32. if hub := GetHub(); hub != nil {
  33. hub.Broadcast(MessageTypeStatus, status)
  34. }
  35. }
  36. // BroadcastTraffic broadcasts traffic statistics update to all connected clients.
  37. func BroadcastTraffic(traffic any) {
  38. if hub := GetHub(); hub != nil {
  39. hub.Broadcast(MessageTypeTraffic, traffic)
  40. }
  41. }
  42. // BroadcastClientStats broadcasts absolute per-client traffic counters. Small
  43. // installs send the complete row set each cycle (payload key snapshot=true);
  44. // above the traffic job's snapshot threshold only the rows active in the
  45. // latest collection window are sent (snapshot=false), which keeps the payload
  46. // under the hub's cap at any client count.
  47. func BroadcastClientStats(stats any) {
  48. if hub := GetHub(); hub != nil {
  49. hub.Broadcast(MessageTypeClientStats, stats)
  50. }
  51. }
  52. // BroadcastInbounds broadcasts inbounds list update to all connected clients.
  53. func BroadcastInbounds(inbounds any) {
  54. if hub := GetHub(); hub != nil {
  55. hub.Broadcast(MessageTypeInbounds, inbounds)
  56. }
  57. }
  58. // BroadcastNodes broadcasts the fresh node list to all connected clients.
  59. // Pushed by NodeHeartbeatJob at the end of each 10s tick so the Nodes page
  60. // reflects status / latency / cpu / mem updates without polling.
  61. func BroadcastNodes(nodes any) {
  62. if hub := GetHub(); hub != nil {
  63. hub.Broadcast(MessageTypeNodes, nodes)
  64. }
  65. }
  66. // BroadcastOutbounds broadcasts outbounds list update to all connected clients.
  67. func BroadcastOutbounds(outbounds any) {
  68. if hub := GetHub(); hub != nil {
  69. hub.Broadcast(MessageTypeOutbounds, outbounds)
  70. }
  71. }
  72. // BroadcastNotification broadcasts a system notification to all connected clients.
  73. func BroadcastNotification(title, message, level string) {
  74. hub := GetHub()
  75. if hub == nil {
  76. return
  77. }
  78. hub.Broadcast(MessageTypeNotification, map[string]string{
  79. "title": title,
  80. "message": message,
  81. "level": level,
  82. })
  83. }
  84. // BroadcastXrayState broadcasts Xray state change to all connected clients.
  85. func BroadcastXrayState(state string, errorMsg string) {
  86. hub := GetHub()
  87. if hub == nil {
  88. return
  89. }
  90. hub.Broadcast(MessageTypeXrayState, map[string]string{
  91. "state": state,
  92. "errorMsg": errorMsg,
  93. })
  94. }
  95. // BroadcastInvalidate sends a lightweight signal telling clients to re-fetch
  96. // the named data type via REST. Use this when the caller already knows the
  97. // payload is too large to push directly (e.g., 10k+ clients) to skip the
  98. // JSON-marshal cost on the hot path.
  99. func BroadcastInvalidate(dataType MessageType) {
  100. if hub := GetHub(); hub != nil {
  101. hub.broadcastInvalidate(dataType)
  102. }
  103. }