package wsapi import ( "encoding/json" "errors" "io" "dynatron.me/x/blasphem/pkg/auth" "dynatron.me/x/blasphem/pkg/blas/core" "github.com/gorilla/websocket" "github.com/labstack/echo/v4" "github.com/rs/zerolog/log" ) var ( NoSuchHandlerErr = errors.New("bad websocket command") ) type Type string type MsgBase struct { Type Type `json:"type"` } type ( wsSession struct { *websocket.Conn b core.Blas ec echo.Context h phaseHandler user *auth.User refreshToken *auth.RefreshToken } phaseHandler interface { handleMsg(io.Reader) error } cmdHandler struct { *wsSession } wsEntry struct { dataNew core.NewData hnd core.Handler } wsRegistry map[string]wsEntry wsManager struct { r wsRegistry } ) func (wsm *wsManager) RegisterWSCommand(cmd string, hnd core.Handler, dataNew core.NewData) { wsm.r[cmd] = wsEntry{ dataNew: dataNew, hnd: hnd, } } func (wsm *wsManager) WSCommandHandler(cmd string) (core.NewData, core.Handler, error) { wse, ok := wsm.r[cmd] if !ok { return nil, nil, NoSuchHandlerErr } return wse.dataNew, wse.hnd, nil } func NewManager() core.WebSocketManager { return &wsManager{ r: make(wsRegistry), } } func NewSession(s core.Blas, c echo.Context, conn *websocket.Conn) core.WebSocketSession { ws := &wsSession{ Conn: conn, b: s, ec: c, } ws.h = &authPhase{ws} return ws } func (ws *wsSession) Blas() core.Blas { return ws.b } func (ws *wsSession) Go() error { err := ws.sendAuthRequired() if err != nil { return err } for { _, rdr, err := ws.NextReader() if err != nil { log.Error().Err(err).Str("remote", ws.ec.Request().RemoteAddr).Msg("websocket read fail") return err } err = ws.h.handleMsg(rdr) if err != nil { return err } } } type cmdMsg struct { } type MsgType string func (ws *cmdHandler) handleMsg(r io.Reader) error { var msgMap map[string]interface{} err := json.NewDecoder(r).Decode(&msgMap) if err != nil { return err } msgType, ok := msgMap["type"].(string) if !ok { return NoSuchHandlerErr } newData, hand, err := ws.b.WSCommandHandler(msgType) if !ok { return err } nd := newData() return hand(ws, nd) }