notifier.go 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  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. func BroadcastNodes(nodes any) {
  59. if hub := GetHub(); hub != nil {
  60. hub.Broadcast(MessageTypeNodes, nodes)
  61. }
  62. }
  63. // BroadcastOutbounds broadcasts outbounds list update to all connected clients.
  64. func BroadcastOutbounds(outbounds any) {
  65. if hub := GetHub(); hub != nil {
  66. hub.Broadcast(MessageTypeOutbounds, outbounds)
  67. }
  68. }
  69. // BroadcastNotification broadcasts a system notification to all connected clients.
  70. func BroadcastNotification(title, message, level string) {
  71. hub := GetHub()
  72. if hub == nil {
  73. return
  74. }
  75. hub.Broadcast(MessageTypeNotification, map[string]string{
  76. "title": title,
  77. "message": message,
  78. "level": level,
  79. })
  80. }
  81. // BroadcastXrayState broadcasts Xray state change to all connected clients.
  82. func BroadcastXrayState(state string, errorMsg string) {
  83. hub := GetHub()
  84. if hub == nil {
  85. return
  86. }
  87. hub.Broadcast(MessageTypeXrayState, map[string]string{
  88. "state": state,
  89. "errorMsg": errorMsg,
  90. })
  91. }
  92. // BroadcastInvalidate sends a lightweight signal telling clients to re-fetch
  93. // the named data type via REST. Use this when the caller already knows the
  94. // payload is too large to push directly (e.g., 10k+ clients) to skip the
  95. // JSON-marshal cost on the hot path.
  96. func BroadcastInvalidate(dataType MessageType) {
  97. if hub := GetHub(); hub != nil {
  98. hub.broadcastInvalidate(dataType)
  99. }
  100. }