From d973502d1187d09d71f44ff134af8bc2f78226c5 Mon Sep 17 00:00:00 2001 From: Antonio De Lucreziis Date: Sat, 25 Jun 2022 02:49:48 +0200 Subject: [PATCH] Major refactor into the /handlers system and wrote a bit of the architecture in the README --- README.md | 50 ++++++++++++++ database/events.go | 7 -- events/events.go | 1 - handlers/handlers.go | 83 +++++++++++++++++++++++ handlers/partita.go | 81 +++++++++++++++++++++++ main.go | 41 ++++++++++-- routes/api.go | 154 +++++++++++++++++-------------------------- routes/auth.go | 19 +++--- routes/pages.go | 43 +++++++----- routes/server.go | 37 ----------- routes/ws.go | 113 +------------------------------ 11 files changed, 348 insertions(+), 281 deletions(-) delete mode 100644 database/events.go delete mode 100644 events/events.go create mode 100644 handlers/handlers.go create mode 100644 handlers/partita.go delete mode 100644 routes/server.go diff --git a/README.md b/README.md index 9edfecc..8acddf5 100644 --- a/README.md +++ b/README.md @@ -11,3 +11,53 @@ $ cd _frontend $ npm run build $ go build ``` + +## Architettura + +Moduli principali (cose che vengono eseguite e fanno cose di _business logic_) + +- [`/main.go`](./main.go) + + Entry point principale del server, qui vengono inizializzate concretamente tutte le dipendenze dei vari moduli (come database, router http e servizio di autenticazione). + + Per ora usiamo un semplice database ed un semplice servizio di autenticazione per le sessioni di accesso che tengono tutto in memoria (più avanti passeremo a SQLite, tanto basterà semplicemente scrivere un'altra implementazione per `database.Database`) + +- [`/routes`](./routes) + + Questo modulo dipende **molto** dal router HTTP in quanto contiene tutte le route del server. Oltre questo dipende solo dal modulo `/handlers` e non sa nulla di `/database` e `/auth`. + +- [`/handlers`](./handlers) + + Questo modulo permette di testare tutta l'applicazione in modo isolato dal resto in quanto non sa nulla dei router HTTP e dipende solo da `/auth` e `/database` (ed anche da `/events`, `/model` e `/util` ma questi sono moduli "puri") che possono essere facilmente _mocked_. + +Moduli secondari (o boh "terminali" nel senso che dipendono solo da cose esterne a questo progetto) + +- [`/events`](./events) + + Questo modulo fornisce una struttura dati di "EventBus" che permette di mandare e ricevere eventi all'interno dell'applicazione. + +- [`/model`](./model) + + Questo modulo contiene i modelli di tutte le strutture usate nel database ed alcuni metodi di servizio come `User.PublicUser()` che converte un `User` in `PublicUser` che rappresenta la versione "sicura" senza i campi segreti (come l'hash della password) dell'utente. + +- [`/database`](./database) + + L'interfaccia principale è `Database` e contiene tutte le operazioni possibili da fare sul database. Per ora c'è solo un'implementazione in memoria data da `*memDB` + +- [`/auth`](./auth) + + L'interfaccia principale è `AuthService` e contiene alcuni metodi per autenticare e registrare gli utenti e creare token di sessione. Per ora l'unica implementazione è `*memAuth` e dipende da un'istanza di `database.Database` e tiene i token di sessione in memoria. + +- [`/util`](./util) + + Questo modulo contiene alcune funzioni di utility e per ora anche varie funzioni di validazione dei form che arrivano dalla frontend, come validazione di username e password per la registrazione degli utenti. + +Ed infine c'è il modulo che si occupa del far avanzare le partite e della risoluzione automatica. + +- [`/lupus`](./lupus) + + Questo modulo non dipende da nulla, l'idea è che conterrà le strutture per gestire lo stato delle partite e gli algoritmi per la risoluzione automatica di quest'ultime. + + Al massimo potrebbe contenere alcune informazioni su come serializzare lo stato delle partite però per ora tutte le strutture qui dentro sono annotate per essere serializzate a JSON ed anche quando passeremo ad SQLite forse converrà fare sempre così. + + TODO: Al massimo potrebbe dipendere da `/model` ma forse non serve. diff --git a/database/events.go b/database/events.go deleted file mode 100644 index abcb114..0000000 --- a/database/events.go +++ /dev/null @@ -1,7 +0,0 @@ -package database - -import "fmt" - -func OnPartitaPlayersChange(partitaUid string) string { - return fmt.Sprintf("partita[uid=%q].players", partitaUid) -} diff --git a/events/events.go b/events/events.go deleted file mode 100644 index b3adf69..0000000 --- a/events/events.go +++ /dev/null @@ -1 +0,0 @@ -package events diff --git a/handlers/handlers.go b/handlers/handlers.go new file mode 100644 index 0000000..6603fc0 --- /dev/null +++ b/handlers/handlers.go @@ -0,0 +1,83 @@ +package handlers + +import ( + "github.com/aziis98/lupus-lite/auth" + "github.com/aziis98/lupus-lite/database" + "github.com/aziis98/lupus-lite/events" + "github.com/aziis98/lupus-lite/model" + "github.com/aziis98/lupus-lite/util" +) + +type Handler interface { + DebugDatabase() any + + UserForSession(token string) (model.User, error) + + Login(username, password string) (string, error) + Register(username, password, password2 string) error + + Partite() PartiteHandler +} + +type server struct { + DB database.Database + Auth auth.AuthService + + partiteHandler *partiteHandler +} + +func NewServer( + db database.Database, + authServiceFunc func(database.Database) auth.AuthService) Handler { + + auth := authServiceFunc(db) + + s := &server{ + DB: db, + Auth: auth, + } + + s.partiteHandler = &partiteHandler{ + server: s, + eventBus: events.NewEventBus(), + } + + return s +} + +func (s *server) DebugDatabase() any { + return s.DB +} + +func (s *server) UserForSession(token string) (model.User, error) { + username, err := s.Auth.UserForSession(token) + if err != nil { + return model.User{}, err + } + + user, err := s.DB.GetUser(username) + if err != nil { + return model.User{}, err + } + + return user, nil +} + +func (s *server) Login(username, password string) (string, error) { + return s.Auth.Login(username, password) +} + +func (s *server) Register(username, password, password2 string) error { + if err := util.ValidateUsername(username); err != nil { + return err + } + if err := util.ValidatePasswords(password, password2); err != nil { + return err + } + + return s.Auth.Register(username, password) +} + +func (s *server) Partite() PartiteHandler { + return s.partiteHandler +} diff --git a/handlers/partita.go b/handlers/partita.go new file mode 100644 index 0000000..b21065e --- /dev/null +++ b/handlers/partita.go @@ -0,0 +1,81 @@ +package handlers + +import ( + "fmt" + + "github.com/aziis98/lupus-lite/events" + "github.com/aziis98/lupus-lite/lupus" + "github.com/aziis98/lupus-lite/model" +) + +func OnPlayerJoin(partitaUid string) string { + return fmt.Sprintf("@join partita[uid=%q]", partitaUid) +} + +type PartitaConfig struct { + NumGiocatori int + NumLupi int + NumFattucchiere int + NumGuardie int + NumCacciatori int + NumMedium int + NumVeggenti int +} + +type PartiteHandler interface { + EventBus() *events.EventBus + + Create(ownerId string, cfg PartitaConfig) (model.Partita, error) + GetPlayers(partitaUid string) ([]string, error) + Join(partitaUid, username string) error +} + +type partiteHandler struct { + *server + + eventBus *events.EventBus +} + +func (p *partiteHandler) Create(ownerId string, cfg PartitaConfig) (model.Partita, error) { + return p.DB.CreatePartita(ownerId, model.PartitaConfig{ + NumeroGiocatori: cfg.NumGiocatori, + NumeroPerRuolo: map[string]int{ + lupus.Lupo.Uid: cfg.NumLupi, + lupus.Fattucchiera.Uid: cfg.NumFattucchiere, + lupus.Guardia.Uid: cfg.NumGuardie, + lupus.Cacciatore.Uid: cfg.NumCacciatori, + lupus.Medium.Uid: cfg.NumMedium, + lupus.Veggente.Uid: cfg.NumVeggenti, + }, + }) +} + +func (p *partiteHandler) GetPlayers(partitaUid string) ([]string, error) { + partita, err := p.DB.GetPartita(partitaUid) + if err != nil { + return nil, err + } + + return partita.Players, nil +} + +func (p *partiteHandler) Join(partitaUid, username string) error { + partita, err := p.DB.GetPartita(partitaUid) + if err != nil { + return err + } + + partita.Players = append(partita.Players, username) + + if err := p.DB.UpdatePartita(partita); err != nil { + return err + } + + p.eventBus.Dispatch(OnPlayerJoin(partitaUid), partita.Players) + + return nil +} + +func (p *partiteHandler) EventBus() *events.EventBus { + return p.eventBus +} diff --git a/main.go b/main.go index 8216a2a..adc5e6d 100644 --- a/main.go +++ b/main.go @@ -6,10 +6,14 @@ import ( "os/exec" "strings" + "github.com/aziis98/lupus-lite/auth" + "github.com/aziis98/lupus-lite/database" + "github.com/aziis98/lupus-lite/handlers" "github.com/aziis98/lupus-lite/routes" "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/middleware/logger" "github.com/gofiber/fiber/v2/middleware/recover" + "github.com/gofiber/websocket/v2" ) func main() { @@ -17,7 +21,12 @@ func main() { app := fiber.New() - server := routes.NewServer() + db, err := database.NewInMemoryDB() + if err != nil { + panic(err) + } + + h := handlers.NewServer(db, auth.NewInMemoryAuthService) app.Use(logger.New()) app.Use(recover.New()) @@ -25,14 +34,36 @@ func main() { // Static files app.Static("/", "./_frontend/dist") - app.Route("/", server.EventRoute) - app.Route("/", server.PageRoutes) - app.Route("/api", server.ApiRoutes) + // Enable WebSocket on routes ending with "/ws" + app.Use("/*/ws", func(c *fiber.Ctx) error { + log.Printf("Upgrading to websocket connection") + if websocket.IsWebSocketUpgrade(c) { + c.Locals("allowed", true) + return c.Next() + } + return fiber.ErrUpgradeRequired + }) + + // + // Routes + // + + app.Route("/", func(r fiber.Router) { + routes.PageRoutes(r, h) + }) + + app.Route("/api", func(r fiber.Router) { + routes.ApiRoutes(r, h) + }) + + // + // Starting the server + // if strings.HasPrefix(mode, "dev") { log.Printf(`Running dev server for frontend: "npm run dev"`) - err := (exec.Command("sh", "-c", "cd _frontend/ && npm run dev").Start()) + err := exec.Command("sh", "-c", "cd _frontend/ && npm run dev").Start() if err != nil { log.Fatal(err) } diff --git a/routes/api.go b/routes/api.go index 8e01c06..d16a100 100644 --- a/routes/api.go +++ b/routes/api.go @@ -3,19 +3,15 @@ package routes import ( "encoding/json" "log" - "strconv" "time" - "github.com/aziis98/lupus-lite/database" - "github.com/aziis98/lupus-lite/lupus" - "github.com/aziis98/lupus-lite/model" - "github.com/aziis98/lupus-lite/util" + "github.com/aziis98/lupus-lite/handlers" "github.com/gofiber/fiber/v2" ) -func (s *Server) ApiRoutes(api fiber.Router) { - api.Get("/status", func(c *fiber.Ctx) error { - s, err := json.MarshalIndent(s.db, "", " ") +func ApiRoutes(r fiber.Router, h handlers.Handler) { + r.Get("/status", func(c *fiber.Ctx) error { + s, err := json.MarshalIndent(h.DebugDatabase(), "", " ") if err != nil { return err } @@ -25,17 +21,17 @@ func (s *Server) ApiRoutes(api fiber.Router) { return c.SendString("ok") }) - api.Post("/login", func(c *fiber.Ctx) error { - var loginForm struct { + r.Post("/login", func(c *fiber.Ctx) error { + var form struct { Username string `form:"username"` Password string `form:"password"` } - if err := c.BodyParser(&loginForm); err != nil { + if err := c.BodyParser(&form); err != nil { return err } - token, err := s.auth.Login(loginForm.Username, loginForm.Password) + token, err := h.Login(form.Username, form.Password) if err != nil { return err } @@ -50,88 +46,71 @@ func (s *Server) ApiRoutes(api fiber.Router) { return c.Redirect("/") }) - api.Post("/logout", func(c *fiber.Ctx) error { - c.Cookie(&fiber.Cookie{ - Name: "sid", - Value: "", - Path: "/", - Expires: time.Now(), + r.Post("/logout", + func(c *fiber.Ctx) error { + c.Cookie(&fiber.Cookie{ + Name: "sid", + Value: "", + Path: "/", + Expires: time.Now(), + }) + + return c.SendString("ok") }) - return c.SendString("ok") - }) + r.Post("/register", + func(c *fiber.Ctx) error { + var form struct { + Username string `form:"username"` + Password string `form:"password"` + Password2 string `form:"password2"` + } - api.Post("/register", func(c *fiber.Ctx) error { - var loginForm struct { - Username string `form:"username"` - Password string `form:"password"` - Password2 string `form:"password2"` - } + if err := c.BodyParser(&form); err != nil { + return err + } - if err := c.BodyParser(&loginForm); err != nil { - return err - } + if err := h.Register(form.Username, form.Password, form.Password2); err != nil { + return err + } - if err := util.ValidateUsername(loginForm.Username); err != nil { - return err - } + return c.Redirect("/login") + }) - if err := util.ValidatePasswords(loginForm.Password, loginForm.Password2); err != nil { - return err - } + r.Get("/user", + RequireLogged(h), + func(c *fiber.Ctx) error { + return c.JSON(requestUser(c).PublicUser()) + }) - if err := s.auth.Register(loginForm.Username, loginForm.Password); err != nil { - return err - } + r.Route("/partite", func(r fiber.Router) { + r.Use(RequireLogged(h)) - return c.Redirect("/login") + ApiPartite(r, h.Partite()) }) +} + +func ApiPartite(r fiber.Router, p handlers.PartiteHandler) { + r.Get("/ws", BindWebsocketToEventBus(p.EventBus())) - api.Post("/crea-partita", s.requireLogged, func(c *fiber.Ctx) error { + r.Post("/", func(c *fiber.Ctx) error { user := requestUser(c) var form struct { - NumGiocatori string `form:"numero-giocatori"` - NumLupi string `form:"numero-lupi"` - NumFattucchiere string `form:"numero-fattucchiere"` - NumGuardie string `form:"numero-guardie"` - NumCacciatori string `form:"numero-cacciatori"` - NumMedium string `form:"numero-medium"` - NumVeggenti string `form:"numero-veggenti"` - } - if err := c.BodyParser(&form); err != nil { - return err + NumGiocatori int `form:"numero-giocatori"` + NumLupi int `form:"numero-lupi"` + NumFattucchiere int `form:"numero-fattucchiere"` + NumGuardie int `form:"numero-guardie"` + NumCacciatori int `form:"numero-cacciatori"` + NumMedium int `form:"numero-medium"` + NumVeggenti int `form:"numero-veggenti"` } - cfg := model.PartitaConfig{ - NumeroPerRuolo: map[string]int{}, - } - - if err := util.AtoiInto(form.NumGiocatori, &cfg.NumeroGiocatori); err != nil { + if err := c.BodyParser(&form); err != nil { return err } - // Questo codice magico scorre su questa "tabella" con colonne ruolo e stringa del ruolo nel form e popola la mappa NumeroPerRuolo da uid ruolo a numero per ruolo. - for _, r := range []struct { - ruolo lupus.Ruolo - num string - }{ - {lupus.Lupo, form.NumLupi}, - {lupus.Fattucchiera, form.NumFattucchiere}, - {lupus.Guardia, form.NumGuardie}, - {lupus.Cacciatore, form.NumCacciatori}, - {lupus.Medium, form.NumMedium}, - {lupus.Veggente, form.NumVeggenti}, - } { - num, err := strconv.Atoi(r.num) - if err != nil { - return err - } - - cfg.NumeroPerRuolo[r.ruolo.Uid] = num - } - - partita, err := s.db.CreatePartita(user.Username, cfg) + partita, err := p.Create(user.Username, handlers.PartitaConfig(form)) if err != nil { return err } @@ -139,38 +118,25 @@ func (s *Server) ApiRoutes(api fiber.Router) { return c.JSON(partita) }) - api.Get("/user", s.requireLogged, func(c *fiber.Ctx) error { - return c.JSON(requestUser(c).PublicUser()) - }) - - api.Get("/partita/:partita/players", func(c *fiber.Ctx) error { + r.Get("/:partita/players", func(c *fiber.Ctx) error { partitaUid := c.Params("partita") - partita, err := s.db.GetPartita(partitaUid) + players, err := p.GetPlayers(partitaUid) if err != nil { return err } - return c.JSON(partita.Players) + return c.JSON(players) }) - api.Get("/partita/:partita/join-partita", s.requireLogged, func(c *fiber.Ctx) error { + r.Get("/:partita/join", func(c *fiber.Ctx) error { user := requestUser(c) partitaUid := c.Params("partita") - partita, err := s.db.GetPartita(partitaUid) - if err != nil { + if err := p.Join(partitaUid, user.Username); err != nil { return err } - partita.Players = append(partita.Players, user.Username) - - if err := s.db.UpdatePartita(partita); err != nil { - return err - } - - s.eventBus.Dispatch(database.OnPartitaPlayersChange(partitaUid), partita.Players) - return c.SendString("ok") }) } diff --git a/routes/auth.go b/routes/auth.go index 015ca4c..7841689 100644 --- a/routes/auth.go +++ b/routes/auth.go @@ -3,7 +3,7 @@ package routes import ( "fmt" - "github.com/aziis98/lupus-lite/auth" + "github.com/aziis98/lupus-lite/handlers" "github.com/aziis98/lupus-lite/model" "github.com/gofiber/fiber/v2" ) @@ -11,26 +11,27 @@ import ( const UserKey = "github.com/aziis98/lupus-lite/user" func requestUser(c *fiber.Ctx) *model.User { - return c.Locals(UserKey).(*model.User) + user, ok := c.Locals(UserKey).(*model.User) // TODO: Check semantics of casting "nil" to pointer to struct + if !ok { + return nil + } + + return user } -func RequireLoggedMiddleware(auth auth.AuthService) fiber.Handler { +func RequireLogged(server handlers.Handler) fiber.Handler { return func(c *fiber.Ctx) error { token := c.Cookies("sid") if token == "" { return fmt.Errorf(`request has no session token`) } - username, err := auth.UserForSession(token) - if err != nil { - return err - } - - user, err := auth.GetUser(username) + user, err := server.UserForSession(token) if err != nil { return err } + // this is wrapped in a pointer to let "nil" mean no user logged in when calling "requestUser()" c.Locals(UserKey, &user) return c.Next() diff --git a/routes/pages.go b/routes/pages.go index b5bb4b0..c690257 100644 --- a/routes/pages.go +++ b/routes/pages.go @@ -1,25 +1,34 @@ package routes -import "github.com/gofiber/fiber/v2" +import ( + "github.com/aziis98/lupus-lite/handlers" + "github.com/gofiber/fiber/v2" +) -func (s *Server) PageRoutes(r fiber.Router) { - r.Get("/", func(c *fiber.Ctx) error { - return c.SendFile("_frontend/dist/index.html") - }) +func PageRoutes(r fiber.Router, h handlers.Handler) { + r.Get("/", + func(c *fiber.Ctx) error { + return c.SendFile("_frontend/dist/index.html") + }) - r.Get("/login", func(c *fiber.Ctx) error { - return c.SendFile("_frontend/dist/login.html") - }) + r.Get("/login", + func(c *fiber.Ctx) error { + return c.SendFile("_frontend/dist/login.html") + }) - r.Get("/register", func(c *fiber.Ctx) error { - return c.SendFile("_frontend/dist/register.html") - }) + r.Get("/register", + func(c *fiber.Ctx) error { + return c.SendFile("_frontend/dist/register.html") + }) - r.Get("/crea-partita", func(c *fiber.Ctx) error { - return c.SendFile("_frontend/dist/crea-partita.html") - }) + r.Get("/crea-partita", + func(c *fiber.Ctx) error { + return c.SendFile("_frontend/dist/crea-partita.html") + }) - r.Get("/p/:partita", s.requireLogged, func(c *fiber.Ctx) error { - return c.SendFile("_frontend/dist/partita.html") - }) + r.Get("/p/:partita", + RequireLogged(h), + func(c *fiber.Ctx) error { + return c.SendFile("_frontend/dist/partita.html") + }) } diff --git a/routes/server.go b/routes/server.go deleted file mode 100644 index ca00d41..0000000 --- a/routes/server.go +++ /dev/null @@ -1,37 +0,0 @@ -package routes - -import ( - "log" - - "github.com/aziis98/lupus-lite/auth" - "github.com/aziis98/lupus-lite/database" - "github.com/aziis98/lupus-lite/events" - "github.com/gofiber/fiber/v2" -) - -type Server struct { - db database.Database - auth auth.AuthService - - // Live Updates - eventBus *events.EventBus - - // Utilities - requireLogged fiber.Handler -} - -func NewServer() *Server { - db, err := database.NewInMemoryDB() - if err != nil { - log.Fatal(err) - } - - auth := auth.NewInMemoryAuthService(db) - - return &Server{ - db: db, - auth: auth, - eventBus: events.NewEventBus(), - requireLogged: RequireLoggedMiddleware(auth), - } -} diff --git a/routes/ws.go b/routes/ws.go index 771b947..e2e8e0e 100644 --- a/routes/ws.go +++ b/routes/ws.go @@ -9,103 +9,6 @@ import ( "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"` @@ -116,8 +19,8 @@ type serverMessage struct { 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 { +// BindWebsocketToEventBus 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 BindWebsocketToEventBus(eb *events.EventBus) fiber.Handler { return websocket.New(func(c *websocket.Conn) { clientId := util.GenerateRandomString(10) @@ -172,15 +75,3 @@ func registerEventBinder(eb *events.EventBus) fiber.Handler { } }) } - -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)) -}