package structs import ( "encoding/json" "errors" "sync" "time" "github.com/gofiber/websocket/v2" "github.com/rs/zerolog/log" ) const ( // Note: Status codes in the range 4000-4999 are reserved for private use SocketCloseCodeUnauthorized = 4001 SocketCloseCodeSessionClosed = 4002 // when user logs out of the session and has multiple tabs with the same session open ) type SocketClient struct { SessionId string UserId string Conn *websocket.Conn connMu sync.Mutex } type SocketMessage struct { Conn *websocket.Conn Msg []byte } type SendSocketMessage struct { Cmd int Body any } type ReceivedMessage struct { Cmd int Body map[string]interface{} } func (socketClient *SocketClient) SendSessionClosedMessage() error { return socketClient.writeMessage(websocket.CloseMessage, SendSocketMessage{}, SocketCloseCodeSessionClosed) } func (socketClient *SocketClient) SendUnauthorizedCloseMessage() error { return socketClient.writeMessage(websocket.CloseMessage, SendSocketMessage{}, SocketCloseCodeUnauthorized) } func (socketClient *SocketClient) SendMessage(message SendSocketMessage) error { return socketClient.writeMessage(websocket.TextMessage, message, 0) } func (socketClient *SocketClient) writeMessage(messageType int, message SendSocketMessage, closeMessageCode int) error { var marshaledMessage []byte var err error if closeMessageCode > 0 { marshaledMessage = websocket.FormatCloseMessage(closeMessageCode, "") } else { marshaledMessage, err = json.Marshal(message) if err != nil { log.Error().Msgf("Failed to marshal ws message, err: %s", err) return err } } socketClient.connMu.Lock() defer socketClient.connMu.Unlock() if socketClient.Conn == nil { log.Error().Msgf("Failed to ws message because conn is nil") return errors.New("ws client conn is nil") } err = socketClient.Conn.WriteMessage(messageType, marshaledMessage) if err != nil { log.Error().Msgf("Failed to write ws message, err: %s", err) return err } return nil } type InitUserSocketConnection struct { User UserData CategoryGroups []CategoryGroup // config specific group tasks GroupTasks []GroupTasks // in database saved group tasks GroupTasksSteps []GroupTaskSteps } type UserData struct { Username string Email string Sessions []UserSessionSocket } type UserSessionSocket struct { IdForDeletion string UserAgent string ConnectionStatus uint8 LastUsed time.Time ExpiresAt time.Time }