package routes import ( "log" "github.com/aziis98/lupus-lite/events" "github.com/aziis98/lupus-lite/util" "github.com/gofiber/fiber/v2" "github.com/gofiber/websocket/v2" ) // func (s *Server) websocketHandler(handler func(c *fiber.Ctx) (interface{}, error)) fiber.Handler { // return websocket.New(func(c *websocket.Conn) { // clientId := util.GenerateRandomString(10) // partitaUid := c.Params("partita") // log.Printf("[%v] Connection started", clientId) // done := make(chan error) // clientListener := s.eventBus.Subscribe( // database.OnPartitaPlayerJoin(partitaUid), // func(e interface{}) { // partita, err := s.db.GetPartita(partitaUid) // if err != nil { // done <- err // return // } // log.Printf(`[%v] Sent message`, clientId) // if err := c.WriteJSON(partita.Players); err != nil { // done <- err // return // } // }, // ) // defer func() { // s.eventBus.Unsubscribe(clientListener) // log.Printf("[%v] Connection closed", clientId) // }() // for { // _, _, err := c.ReadMessage() // if err != nil { // if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway, websocket.CloseAbnormalClosure) { // log.Printf("[%v] Error: %v", clientId, err) // } // return // } // select { // case err := <-done: // if err != nil { // log.Printf(`[%v] Error: %v`, clientId, err) // } // return // case <-time.After(1 * time.Second): // } // } // }) // } // func (s *Server) registerEventBinding(event string, process func(c *fiber.Ctx, e any) (any, error)) fiber.Handler { // return websocket.New(func(c *websocket.Conn) { // clientId := util.GenerateRandomString(10) // log.Printf("[%v] Connection started", clientId) // done := make(chan error) // clientListener := s.eventBus.Subscribe( // event, // func(e interface{}) { // partita, err := s.db.GetPartita(partitaUid) // if err != nil { // done <- err // return // } // log.Printf(`[%v] Sent message`, clientId) // if err := c.WriteJSON(partita.Players); err != nil { // done <- err // return // } // }, // ) // defer func() { // s.eventBus.Unsubscribe(clientListener) // log.Printf("[%v] Connection closed", clientId) // }() // for { // _, _, err := c.ReadMessage() // if err != nil { // if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway, websocket.CloseAbnormalClosure) { // log.Printf("[%v] Error: %v", clientId, err) // } // return // } // select { // case err := <-done: // if err != nil { // log.Printf(`[%v] Error: %v`, clientId, err) // } // return // case <-time.After(1 * time.Second): // } // } // }) // } type clientMessage struct { Type string `json:"type"` Event string `json:"event"` } type serverMessage struct { Event string `json:"event"` Data any `json:"data"` } // registerEventBinder creates a WebSocket handler that can subscribe the socket to some events generated on the server, for example a message from the client like { type: "subscribe", event: "foo" } binds the connection to the event "foo" and forwards events and the associated data to client as JSON. func registerEventBinder(eb *events.EventBus) fiber.Handler { return websocket.New(func(c *websocket.Conn) { clientId := util.GenerateRandomString(10) log.Printf("[%v] Connection started", clientId) defer log.Printf("[%v] Connection closed", clientId) listeners := []*events.Listener{} defer func() { for _, l := range listeners { eb.Unsubscribe(l) } }() done := make(chan error) for { var msg clientMessage if err := c.ReadJSON(&msg); err != nil { log.Printf(`[%v] Read error: %v`, clientId, err) return } log.Printf("[%v] Received: %v", clientId, msg) switch msg.Type { case "subscribe": newListener := eb.Subscribe( msg.Event, func(e interface{}) { log.Printf(`[%v] Sent message`, clientId) if err := c.WriteJSON(serverMessage{ Event: msg.Event, Data: e, }); err != nil { log.Printf(`[%v] Write error: %v`, clientId, err) done <- err } }, ) listeners = append(listeners, newListener) default: log.Printf("[%v] Unknown message type %q", clientId, msg.Type) return } select { case err := <-done: if err != nil { return } default: } } }) } func (s *Server) EventRoute(r fiber.Router) { r.Use("/ws", func(c *fiber.Ctx) error { if websocket.IsWebSocketUpgrade(c) { c.Locals("allowed", true) return c.Next() } return fiber.ErrUpgradeRequired }) r.Get("/ws", registerEventBinder(s.eventBus)) }