1
0

notifier.go 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. // Package websocket provides WebSocket hub for real-time updates and notifications.
  2. package websocket
  3. import (
  4. "github.com/mhsanaei/3x-ui/v2/logger"
  5. "github.com/mhsanaei/3x-ui/v2/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 for the
  43. // clients that had activity in the latest collection window. Use this instead
  44. // of re-broadcasting the full inbound list — it scales to 10k+ clients because
  45. // the payload only includes active rows (typically a fraction of total).
  46. func BroadcastClientStats(stats any) {
  47. if hub := GetHub(); hub != nil {
  48. hub.Broadcast(MessageTypeClientStats, stats)
  49. }
  50. }
  51. // BroadcastInbounds broadcasts inbounds list update to all connected clients.
  52. func BroadcastInbounds(inbounds any) {
  53. if hub := GetHub(); hub != nil {
  54. hub.Broadcast(MessageTypeInbounds, inbounds)
  55. }
  56. }
  57. // BroadcastOutbounds broadcasts outbounds list update to all connected clients.
  58. func BroadcastOutbounds(outbounds any) {
  59. if hub := GetHub(); hub != nil {
  60. hub.Broadcast(MessageTypeOutbounds, outbounds)
  61. }
  62. }
  63. // BroadcastNotification broadcasts a system notification to all connected clients.
  64. func BroadcastNotification(title, message, level string) {
  65. hub := GetHub()
  66. if hub == nil {
  67. return
  68. }
  69. hub.Broadcast(MessageTypeNotification, map[string]string{
  70. "title": title,
  71. "message": message,
  72. "level": level,
  73. })
  74. }
  75. // BroadcastXrayState broadcasts Xray state change to all connected clients.
  76. func BroadcastXrayState(state string, errorMsg string) {
  77. hub := GetHub()
  78. if hub == nil {
  79. return
  80. }
  81. hub.Broadcast(MessageTypeXrayState, map[string]string{
  82. "state": state,
  83. "errorMsg": errorMsg,
  84. })
  85. }
  86. // BroadcastInvalidate sends a lightweight signal telling clients to re-fetch
  87. // the named data type via REST. Use this when the caller already knows the
  88. // payload is too large to push directly (e.g., 10k+ clients) to skip the
  89. // JSON-marshal cost on the hot path.
  90. func BroadcastInvalidate(dataType MessageType) {
  91. if hub := GetHub(); hub != nil {
  92. hub.broadcastInvalidate(dataType)
  93. }
  94. }