Documentation
¶
Index ¶
- Constants
- Variables
- func CookieFromRequest(session Session, httpOnly bool, path string) *http.Cookie
- func DecodeEvent(r io.Reader, e *Event) error
- func EncodeEvent(w io.Writer, e *Event) error
- func JWTMiddleware(a AuthStore) router.Middleware
- func NewToken(user UserWithoutSecrets, expiration time.Duration, secret []byte) (string, time.Time, error)
- type AuthClaims
- type AuthOptions
- type AuthStore
- type AutoIncrementConnIDGenerator
- type ChatStore
- type ChatType
- type Conn
- type ConnIDGenerator
- type ConnManager
- func (m *ConnManager) Connect(username string, w http.ResponseWriter, r *http.Request) error
- func (m *ConnManager) IsUserConnected(username string) bool
- func (m *ConnManager) OnConnectionClosed(f func(context.Context, string, int))
- func (m *ConnManager) OnConnectionOpened(f func(context.Context, string, int))
- func (m *ConnManager) OnUserConnected(f func(context.Context, string))
- func (m *ConnManager) OnUserDisconnected(f func(context.Context, string))
- func (m *ConnManager) Receive() <-chan *Event
- func (m *ConnManager) Send(e *Event)
- func (m *ConnManager) SendToConn(e *Event, username string, id int)
- func (m *ConnManager) SendToUsers(e *Event, username ...string)
- type Error
- type Event
- type EventHandler
- type EventRouter
- func (em *EventRouter) Close(ctx context.Context)
- func (em *EventRouter) Emit(t string, payload interface{}) error
- func (em *EventRouter) EmitTo(t string, payload interface{}, usernames ...string) error
- func (em *EventRouter) Listen()
- func (em *EventRouter) On(eventName string, handler EventHandler)
- type EventTransport
- type GetUsersOptions
- type HttpAuth
- type ManagerOption
- type MemberRole
- type Message
- type MessageCreateInput
- type MessageType
- type NamespaceTree
- type NamnespaceNode
- type Room
- type RoomMember
- type RoomSummary
- type SQLiteAuthStore
- type SQLiteChatStore
- func (s *SQLiteChatStore) AddRoomMember(ctx context.Context, roomID, username string, role MemberRole) error
- func (s *SQLiteChatStore) AreFriends(ctx context.Context, username1, username2 string) (bool, error)
- func (s *SQLiteChatStore) CreateRoom(ctx context.Context, name string, ownerUsername string) (string, error)
- func (s *SQLiteChatStore) GetFriends(ctx context.Context, username string) ([]string, error)
- func (s *SQLiteChatStore) GetRoomByID(ctx context.Context, roomID string) (*Room, error)
- func (s *SQLiteChatStore) GetRoomMembers(ctx context.Context, roomID string) ([]RoomMember, error)
- func (s *SQLiteChatStore) GetRoomMessages(ctx context.Context, roomID string, offset, limit int) ([]Message, error)
- func (s *SQLiteChatStore) GetRoomSummaries(ctx context.Context, user string, offset, limit int) ([]RoomSummary, error)
- func (s *SQLiteChatStore) GetUserRooms(ctx context.Context, user string, offset, litmit int) ([]Room, error)
- func (s *SQLiteChatStore) IsRoomMember(ctx context.Context, roomID, user string) (bool, MemberRole, error)
- func (s *SQLiteChatStore) ReadRoomMessages(ctx context.Context, roomID, user string) (int, time.Time, error)
- func (s *SQLiteChatStore) RemoveRoomMember(ctx context.Context, roomID, username string) error
- func (s *SQLiteChatStore) SendMessageToRoom(ctx context.Context, message MessageCreateInput) (*Message, error)
- type SQLiteDB
- type SQLiteDBOption
- type SQLiteUserStore
- func (s *SQLiteUserStore) ComparePassword(ctx context.Context, username, password string) (bool, error)
- func (s *SQLiteUserStore) CreateUser(ctx context.Context, user User) error
- func (s *SQLiteUserStore) GetUserByUsername(ctx context.Context, username string) (*UserWithoutSecrets, error)
- func (s *SQLiteUserStore) GetUsers(ctx context.Context, options *GetUsersOptions) ([]UserWithoutSecrets, error)
- func (s *SQLiteUserStore) GetUsersByUsernames(ctx context.Context, usernames ...string) ([]UserWithoutSecrets, error)
- type Session
- type SyncMap
- func (s *SyncMap[K, V]) Delete(key K)
- func (s *SyncMap[K, V]) Load(key K) (value V, ok bool)
- func (s *SyncMap[K, V]) LoadAndStore(key K, f func(value V, ok bool) V)
- func (s *SyncMap[K, V]) RRange(f func(key K, value V) bool)
- func (s *SyncMap[K, V]) Store(key K, value V)
- func (s *SyncMap[K, V]) WRange(f func(key K, value V) bool)
- type User
- type UserStore
- type UserWithoutSecrets
- type WSMessage
- type WSMessageWithSender
- type WSPeerType
- type WSState
Constants ¶
const ( WSOpened WSState = iota WSOpening WSClosed WSClosing WSServer WSPeerType = iota WSClient )
const (
AuthCookieName = "auth_token"
)
Variables ¶
var ( ErrBadCredentials = errors.New("invalid credentials") ErrUnauthenticated = errors.New("unauthenticated") )
var ( // ErrInvalidUser is returned when a user is not found or is invalid. ErrInvalidUser = errors.New("invalid user") // ErrConflictedRoom is returned when a private chat room already exists between two users. ErrConflictedRoom = errors.New("chat already exists") // ErrInvalidRoom is returned when a chat room is not found for ErrInvalidRoom = errors.New("invalid room") // ErrInvalidMessage is returned when a message is invalid. ErrInvalidMessage = errors.New("invalid message") // ErrInvalidMessageType is returned when the type of the message is not supported. ErrInvalidMessageType = errors.New("invalid message type") // ErrInsufficientUsers is returned when insufficient users are provided to create a room. ErrInsufficientUsers = errors.New("insufficient users") ErrDisAllowedOperation = errors.New("disallowed operation") ErrInvalidMember = errors.New("invalid member") )
var ( ErrTokenExpired = errors.New("token expired") ErrTokenInvalid = errors.New("token invalid") ErrUnrecognizedToken = errors.New("unrecognized token") )
var (
ErrConflictedUser = errors.New("user already exists")
)
Functions ¶
func CookieFromRequest ¶
func JWTMiddleware ¶
func JWTMiddleware(a AuthStore) router.Middleware
JWTMiddleware extracts the JWT token from the request and validates it and attaches the session to the request context. The session is gaurenteed to be attached to the request context if the JWT token is valid for subsequent handlers.
Types ¶
type AuthClaims ¶
type AuthClaims struct {
Username string
jwt.RegisteredClaims
}
func NewClaim ¶
func NewClaim(user UserWithoutSecrets, exp time.Time) *AuthClaims
func VerifyToken ¶
func VerifyToken(token string, secret []byte) (*AuthClaims, error)
type AuthOptions ¶
type AuthOptions func(*SQLiteAuthStore)
func WithTokenExp ¶
func WithTokenExp(exp time.Duration) AuthOptions
type AutoIncrementConnIDGenerator ¶
type AutoIncrementConnIDGenerator struct {
// contains filtered or unexported fields
}
type ChatStore ¶
type ChatStore interface {
// CreateRoom creates a chat room with the given name and users.
// If the one of the users does not exist, it returns ErrInvalidUser.
// If the number of users is less than 2, it returns ErrInvalidUser.
// If there are duplicate users, it is deduplicated.
// If the error is nil, it returns the ID of the created room.
CreateRoom(ctx context.Context, name, owner string) (string, error)
AddRoomMember(ctx context.Context, roomID string, user string, role MemberRole) error
RemoveRoomMember(ctx context.Context, roomID string, user string) error
GetUserRooms(ctx context.Context, user string, offset, litmit int) ([]Room, error)
// GetRoomByID returns the room with the given ID.
// If the room is not found, it returns nil.
GetRoomByID(ctx context.Context, roomID string) (*Room, error)
// GetRoomSummaries returns a list of room summaries for the given user.
// The rooms are ordered by the last message sent time to the room then room name.
// Reading offset and limit can be specified to paginate the results.
// If the limit is a zero value, the limit is set to 100.
// A nil slice is returned if there are no rooms.
GetRoomSummaries(ctx context.Context, user string, offset, limit int) ([]RoomSummary, error)
// SendMessageToRoom sends a message to the room.
// If the user is not a member of the room, it returns ErrInvalidRoom.
// If the message type is not supported, it returns ErrInvaidMessageType.
// If the message is invalid, it returns ErrInvalidMessage.
// The validity of the message is determined by the MessageCreateInput.Validate method.
// Sender's last read message will be set to the message ID. This assumes that the sender
// has read all previous messages in the room.
SendMessageToRoom(ctx context.Context, message MessageCreateInput) (*Message, error)
// GetRoomMessages returns a list of messages in the room ordered in descending order of sent_at.
// Reading offset and limit can be specified to paginate the results.
// If the limit is a zero value, the limit is set to 100.
GetRoomMessages(ctx context.Context, roomID string, offset, limit int) ([]Message, error)
// IsRoomMember returns true and the role of that membert if the user is a member of the room.
IsRoomMember(ctx context.Context, roomID, user string) (bool, MemberRole, error)
// ReadRoomMessages marks the messages in the room as read up to a message.
// It returns the message ID of the last message read and the time the messages were read.
ReadRoomMessages(ctx context.Context, roomID, user string) (int, time.Time, error)
// GetRoomMembers returns a list of members in the room.
// If the room is not found, it returns nil.
GetRoomMembers(ctx context.Context, roomID string) ([]RoomMember, error)
// GetFriends returns a list of friends for the given user.
// A friend is a user that are member of at least one common chat room.
GetFriends(ctx context.Context, username string) ([]string, error)
// AreFriends returns true if the two users are friends.
AreFriends(ctx context.Context, user1, user2 string) (bool, error)
}
type ConnIDGenerator ¶
type ConnManager ¶
type ConnManager struct {
ReadStreamSize int
WriteStreamSize int
// contains filtered or unexported fields
}
func NewConnManager ¶
func NewConnManager(ctx context.Context, wg *sync.WaitGroup, logger *slog.Logger, opts ...ManagerOption) *ConnManager
func (*ConnManager) Connect ¶
func (m *ConnManager) Connect(username string, w http.ResponseWriter, r *http.Request) error
func (*ConnManager) IsUserConnected ¶
func (m *ConnManager) IsUserConnected(username string) bool
func (*ConnManager) OnConnectionClosed ¶
func (m *ConnManager) OnConnectionClosed(f func(context.Context, string, int))
func (*ConnManager) OnConnectionOpened ¶
func (m *ConnManager) OnConnectionOpened(f func(context.Context, string, int))
func (*ConnManager) OnUserConnected ¶
func (m *ConnManager) OnUserConnected(f func(context.Context, string))
func (*ConnManager) OnUserDisconnected ¶
func (m *ConnManager) OnUserDisconnected(f func(context.Context, string))
func (*ConnManager) Receive ¶
func (m *ConnManager) Receive() <-chan *Event
func (*ConnManager) Send ¶
func (m *ConnManager) Send(e *Event)
func (*ConnManager) SendToConn ¶
func (m *ConnManager) SendToConn(e *Event, username string, id int)
func (*ConnManager) SendToUsers ¶
func (m *ConnManager) SendToUsers(e *Event, username ...string)
type Error ¶
type Error struct {
// sensitive is a flag to indicate if the error is sensitive or not.
// If it is not, it can be returned to the client.
Sensitive bool
// contains filtered or unexported fields
}
func NewInsensitiveError ¶
func NewSensitiveError ¶
type Event ¶
type Event struct {
ID int `json:"-"`
Dispatcher string `json:"-"`
Type string `json:"type"`
Payload json.RawMessage `json:"payload"`
}
type EventRouter ¶
type EventRouter struct {
// contains filtered or unexported fields
}
func NewEventRouter ¶
func NewEventRouter(ctx context.Context, logger *slog.Logger, transport EventTransport) *EventRouter
func (*EventRouter) Close ¶
func (em *EventRouter) Close(ctx context.Context)
func (*EventRouter) Emit ¶
func (em *EventRouter) Emit(t string, payload interface{}) error
Emit sends an event to the specified targets.
func (*EventRouter) EmitTo ¶
func (em *EventRouter) EmitTo(t string, payload interface{}, usernames ...string) error
func (*EventRouter) Listen ¶
func (em *EventRouter) Listen()
func (*EventRouter) On ¶
func (em *EventRouter) On(eventName string, handler EventHandler)
type EventTransport ¶
type GetUsersOptions ¶
type GetUsersOptions struct {
// contains filtered or unexported fields
}
type ManagerOption ¶
type ManagerOption func(*ConnManager)
func WithCheckOrigin ¶
func WithCheckOrigin(f func(r *http.Request) bool) ManagerOption
func WithLogger ¶
func WithLogger(l *slog.Logger) ManagerOption
type MemberRole ¶
type MemberRole string
const ( Owner MemberRole = "owner" Admin MemberRole = "admin" Member MemberRole = "member" )
type Message ¶
type Message struct {
ID int `json:"id"`
// Type is used to determined how the message data should be interpreted.
Type MessageType `json:"type"`
Data string `json:"data"`
RoomID string `json:"room_id"`
Sender string `json:"sender"`
SentAt time.Time `json:"sent_at"`
}
Message represents a chat message sent by a user to a room.
type MessageCreateInput ¶
type MessageCreateInput struct {
Type MessageType `json:"type" validate:"required"`
Data string `json:"data" validate:"required"`
Sender string `json:"sender" validate:"required"`
RoomID string `json:"room_id" validate:"required"`
}
MessageCreateInput represents the input for creating a message.
func (*MessageCreateInput) Validate ¶
func (m *MessageCreateInput) Validate() error
Validate validates the message input.
type MessageType ¶
type MessageType = int
MessageType represents the type of a chat message. It is used to determine how the message data should be interpreted.
const ( // TextMessage indicates that the message is a text message. // The Data field of the message should be interpreted as a UTF-8 encoded string. TextMessage MessageType )
type NamespaceTree ¶
type NamespaceTree struct {
}
type NamnespaceNode ¶
type NamnespaceNode struct {
}
type Room ¶
type Room struct {
ID string `json:"id"`
Members []RoomMember `json:"members"`
Name string `json:"name"`
LastMessageSentAt time.Time `json:"last_message_sent_at"`
LastMessageSent int `json:"last_message_sent"`
LastMessageSentData string `json:"last_message_sent_data"`
}
Room represents a chat room.
type RoomMember ¶
type RoomMember struct {
Role MemberRole `json:"role"`
Username string `json:"username"`
RoomID string `json:"room_id"`
LastMessageRead int `json:"last_message_read"`
}
RoomUser represents a user in a chat room. It is used to store additional information about the room that is specific to the user.
type RoomSummary ¶
type RoomSummary struct {
ID string `json:"id"`
Name string `json:"name"`
Members []string `json:"members"`
LastMessageRead int `json:"last_message_read"`
}
RoomSummary represents a summary of a chat room from the perspective of a member.
type SQLiteAuthStore ¶
type SQLiteAuthStore struct {
// contains filtered or unexported fields
}
func NewSQLiteAuthStore ¶
func NewSQLiteAuthStore(db *sql.DB, userStore UserStore, secret []byte, opts ...AuthOptions) *SQLiteAuthStore
func (*SQLiteAuthStore) DestroySession ¶
func (a *SQLiteAuthStore) DestroySession(ctx context.Context, session Session) error
func (*SQLiteAuthStore) NewSession ¶
type SQLiteChatStore ¶
type SQLiteChatStore struct {
// contains filtered or unexported fields
}
func NewSQLiteChatStore ¶
func NewSQLiteChatStore(db *sql.DB, userStore UserStore) *SQLiteChatStore
func (*SQLiteChatStore) AddRoomMember ¶
func (s *SQLiteChatStore) AddRoomMember(ctx context.Context, roomID, username string, role MemberRole) error
func (*SQLiteChatStore) AreFriends ¶
func (*SQLiteChatStore) CreateRoom ¶
func (*SQLiteChatStore) GetFriends ¶
GetConnectedUsers returns a list of friends of the user. A friend is a user that has a private chat or are part of a group chat with the user.
func (*SQLiteChatStore) GetRoomByID ¶
func (*SQLiteChatStore) GetRoomMembers ¶
func (s *SQLiteChatStore) GetRoomMembers(ctx context.Context, roomID string) ([]RoomMember, error)
func (*SQLiteChatStore) GetRoomMessages ¶
func (*SQLiteChatStore) GetRoomSummaries ¶
func (s *SQLiteChatStore) GetRoomSummaries(ctx context.Context, user string, offset, limit int) ([]RoomSummary, error)
func (*SQLiteChatStore) GetUserRooms ¶
func (*SQLiteChatStore) IsRoomMember ¶
func (s *SQLiteChatStore) IsRoomMember(ctx context.Context, roomID, user string) (bool, MemberRole, error)
func (*SQLiteChatStore) ReadRoomMessages ¶
func (*SQLiteChatStore) RemoveRoomMember ¶
func (s *SQLiteChatStore) RemoveRoomMember(ctx context.Context, roomID, username string) error
func (*SQLiteChatStore) SendMessageToRoom ¶
func (s *SQLiteChatStore) SendMessageToRoom(ctx context.Context, message MessageCreateInput) (*Message, error)
type SQLiteDB ¶
func NewSQLiteDB ¶
func NewSQLiteDB(file, migrationDir string, config *SQLiteDBOption) (*SQLiteDB, error)
type SQLiteDBOption ¶
type SQLiteDBOption struct {
// mode can be ro | rw | rwc | memory
Mode string
// cache can be shared | private
Cache string
// JournalMode be DELETE | TRUNCATE | PERSIST | MEMORY | WAL | OFF
JournalMode string
}
func (*SQLiteDBOption) DSN ¶
func (config *SQLiteDBOption) DSN(sb *strings.Builder)
type SQLiteUserStore ¶
type SQLiteUserStore struct {
// contains filtered or unexported fields
}
func NewSqlieUserStore ¶
func NewSqlieUserStore(db *sql.DB) *SQLiteUserStore
func (*SQLiteUserStore) ComparePassword ¶
func (*SQLiteUserStore) CreateUser ¶
func (s *SQLiteUserStore) CreateUser(ctx context.Context, user User) error
func (*SQLiteUserStore) GetUserByUsername ¶
func (s *SQLiteUserStore) GetUserByUsername(ctx context.Context, username string) (*UserWithoutSecrets, error)
func (*SQLiteUserStore) GetUsers ¶
func (s *SQLiteUserStore) GetUsers(ctx context.Context, options *GetUsersOptions) ([]UserWithoutSecrets, error)
func (*SQLiteUserStore) GetUsersByUsernames ¶
func (s *SQLiteUserStore) GetUsersByUsernames(ctx context.Context, usernames ...string) ([]UserWithoutSecrets, error)
type Session ¶
type Session struct {
Username string `json:"username"`
Token string `json:"token"`
ExpiresAt time.Time `json:"expires_at"`
}
func SessionFromRequest ¶
SessionFromRequest extracts the session from the request context. It must be called in handlers that are protected by the JWTMiddleware. It panics if the session is not found in the request context.
type SyncMap ¶
type SyncMap[K comparable, V any] struct { // contains filtered or unexported fields }
SyncMap is an implementation of a map that is safe for concurrent usage.
func NewSyncMap ¶
func NewSyncMap[K comparable, V any]() *SyncMap[K, V]
func (*SyncMap[K, V]) LoadAndStore ¶
LoadAndStore retrieves the value for a key, applies the function f to it, and stores the result. It guarantees that the whole operation is atomic.
type User ¶
type UserStore ¶
type UserStore interface {
CreateUser(ctx context.Context, user User) error
GetUserByUsername(ctx context.Context, username string) (*UserWithoutSecrets, error)
GetUsersByUsernames(ctx context.Context, usernames ...string) ([]UserWithoutSecrets, error)
ComparePassword(ctx context.Context, username, password string) (bool, error)
GetUsers(ctx context.Context, opts *GetUsersOptions) ([]UserWithoutSecrets, error)
}
type UserWithoutSecrets ¶
type WSMessage ¶
func NewWSMessage ¶
type WSMessageWithSender ¶
func NewWSMessageWithSender ¶
func NewWSMessageWithSender(sender string, m *WSMessage) *WSMessageWithSender
type WSPeerType ¶
type WSPeerType int