mengstack-api/internal/kernel/websocket/handler.go
MengStack Dev df809f1045
Some checks failed
CI / Build & Test (push) Failing after 1m31s
feat: user CRUD API + dashboard stats + kernel infrastructure
- Add user management endpoints (list/create/get/update/delete) with pagination and search
- Add dashboard stats endpoint with tenant/user/online counts and growth metrics
- Add tenant resolver middleware for multi-tenant request scoping
- Add i18n kernel with zh/en message files and AcceptLanguage middleware
- Add WebSocket hub/handler for real-time communication
- Add job scheduler kernel with cron support
- Add plugin sandbox for isolated execution
- Add storage kernel (local filesystem)
- Add event bus kernel for pub/sub
- Add cache kernel abstraction
- Add database migration runner and version upgrade checker
- Add rate limiting middleware with Redis backend
- Add SQL migrations for rbac, audit_logs, settings, notifications, examples
- Extend user repository with list/delete/count operations
- Register all module routes with tenant resolver
2026-10-03 03:42:58 +08:00

103 lines
1.9 KiB
Go

package websocket
import (
"net/http"
"time"
"github.com/gin-gonic/gin"
"github.com/gorilla/websocket"
"go.uber.org/zap"
)
var upgrader = websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
CheckOrigin: func(r *http.Request) bool {
return true
},
}
type Handler struct {
hub *Hub
log *zap.Logger
}
func NewHandler(hub *Hub, log *zap.Logger) *Handler {
return &Handler{hub: hub, log: log}
}
func (h *Handler) ServeWS(c *gin.Context) {
userID := c.GetUint("user_id")
tenantID := c.GetString("tenant_id")
if userID == 0 {
c.AbortWithStatusJSON(401, gin.H{"code": -1, "message": "unauthorized"})
return
}
conn, err := upgrader.Upgrade(c.Writer, c.Request, nil)
if err != nil {
h.log.Error("websocket upgrade", zap.Error(err))
return
}
client := NewClient(h.hub, userID, tenantID)
h.hub.Register(client)
go client.writePump(conn)
go client.readPump(conn, h.hub)
}
func (c *Client) readPump(conn *websocket.Conn, hub *Hub) {
defer func() {
hub.Unregister(c)
conn.Close()
close(c.done)
}()
conn.SetReadLimit(maxMessageSize)
conn.SetReadDeadline(deadline(pongWait))
conn.SetPongHandler(func(string) error {
conn.SetReadDeadline(deadline(pongWait))
return nil
})
for {
_, _, err := conn.ReadMessage()
if err != nil {
break
}
}
}
func (c *Client) writePump(conn *websocket.Conn) {
ticker := time.NewTicker(pingPeriod)
defer func() {
ticker.Stop()
conn.Close()
}()
for {
select {
case msg, ok := <-c.send:
conn.SetWriteDeadline(deadline(writeWait))
if !ok {
conn.WriteMessage(websocket.CloseMessage, []byte{})
return
}
if err := conn.WriteMessage(websocket.TextMessage, msg); err != nil {
return
}
case <-ticker.C:
conn.SetWriteDeadline(deadline(writeWait))
if err := conn.WriteMessage(websocket.PingMessage, nil); err != nil {
return
}
}
}
}
func deadline(d time.Duration) time.Time {
return time.Now().Add(d)
}