414 lines
13 KiB
Go
414 lines
13 KiB
Go
package types
|
|
|
|
import (
|
|
"crypto/sha256"
|
|
"math"
|
|
"math/rand/v2"
|
|
"sync"
|
|
"time"
|
|
|
|
"go-socket/packages/Enums/ConnectionState"
|
|
"go-socket/packages/Enums/WsEventType"
|
|
"go-socket/packages/config"
|
|
|
|
"github.com/coder/websocket"
|
|
"github.com/google/uuid"
|
|
)
|
|
|
|
type Rgba [4]uint8
|
|
type Sha256Hash [sha256.Size]byte
|
|
|
|
func RandomRgba() Rgba {
|
|
var r Rgba
|
|
for i := range r {
|
|
r[i] = uint8(rand.IntN(256))
|
|
}
|
|
return r
|
|
}
|
|
|
|
type User struct {
|
|
Mu sync.RWMutex `json:"-"`
|
|
Name string `json:"name"`
|
|
Pronouns string `json:"pronouns"`
|
|
Description string `json:"description"`
|
|
AvatarKey string `json:"avatarType"`
|
|
ProfileBgKey string `json:"profileBackgroundType"`
|
|
PasswordHash string `json:"-"`
|
|
CreatedAt time.Time `json:"createdAt"`
|
|
WsConn *websocket.Conn `json:"-"`
|
|
Id uuid.UUID `json:"-"`
|
|
Connections map[uuid.UUID]*Connection `json:"-"`
|
|
Hubs map[uuid.UUID]*Hub `json:"hubs-to-delete"`
|
|
Color Rgba `json:"color"`
|
|
}
|
|
|
|
type UserProfileUpdate struct {
|
|
Pronouns bool
|
|
Description bool
|
|
Color bool
|
|
Avatar bool
|
|
ProfileBg bool
|
|
}
|
|
|
|
type Connection struct {
|
|
Mu sync.RWMutex `json:"-"`
|
|
Id uuid.UUID `json:"id"`
|
|
CreatedAt time.Time `json:"createdAt"`
|
|
MessagesBuff []*Message `json:"-"`
|
|
NextBuffIdx uint32 `json:"-"`
|
|
RequestorId uuid.UUID `json:"requestorId"`
|
|
RecipientId uuid.UUID `json:"recipientId"`
|
|
UserWantingToElevate uuid.UUID `json:"userWantingToElevate"` // TODO add to database
|
|
HaveOverflowed bool `json:"-"`
|
|
State ConnectionState.ConnectionState `json:"state"`
|
|
}
|
|
|
|
func NewConn() *Connection {
|
|
return &Connection{
|
|
MessagesBuff: make([]*Message, config.MaxDirectMsgCache),
|
|
}
|
|
}
|
|
func (conn *Connection) AddMessageToBuff(message *Message) {
|
|
conn.Mu.Lock()
|
|
defer conn.Mu.Unlock()
|
|
|
|
size := uint32(len(conn.MessagesBuff))
|
|
conn.MessagesBuff[conn.NextBuffIdx%size] = message
|
|
conn.NextBuffIdx++
|
|
if conn.NextBuffIdx >= size {
|
|
conn.HaveOverflowed = true
|
|
}
|
|
}
|
|
|
|
// GetSortedMessagesBuff returns slice and its valid length.
|
|
func (conn *Connection) GetSortedMessagesBuff() ([]*Message, uint32) {
|
|
conn.Mu.RLock()
|
|
defer conn.Mu.RUnlock()
|
|
|
|
size := uint32(len(conn.MessagesBuff))
|
|
if !conn.HaveOverflowed {
|
|
return conn.MessagesBuff, conn.NextBuffIdx
|
|
}
|
|
|
|
sorted := make([]*Message, size)
|
|
for i := uint32(0); i < size; i++ {
|
|
sorted[i] = conn.MessagesBuff[(conn.NextBuffIdx+i)%size]
|
|
}
|
|
return sorted, size
|
|
}
|
|
|
|
func (conn *Connection) GetSecondUser(id uuid.UUID) uuid.UUID {
|
|
if id == conn.RequestorId {
|
|
return conn.RecipientId
|
|
}
|
|
return conn.RequestorId
|
|
}
|
|
|
|
type ConnectionStatusSetData struct {
|
|
Id uuid.UUID `json:"id"`
|
|
NewState ConnectionState.ConnectionState `json:"newState"`
|
|
}
|
|
|
|
type ConnectionElevatePendingData struct {
|
|
Id uuid.UUID `json:"id"`
|
|
UserWantingToElevate uuid.UUID `json:"userWantingToElevate"`
|
|
}
|
|
|
|
type Message struct {
|
|
Id uuid.UUID `json:"id"`
|
|
AttachedFile string `json:"attachedFile"`
|
|
Content string `json:"content"`
|
|
CreatedAt time.Time `json:"createdAt"`
|
|
Sender uuid.UUID `json:"sender"`
|
|
Receiver uuid.UUID `json:"receiver"`
|
|
}
|
|
|
|
type LoginResponse struct {
|
|
Token string `json:"token"`
|
|
UserId uuid.UUID `json:"userId"`
|
|
}
|
|
|
|
type WsEventMessage struct {
|
|
Type WsEventType.WsEventType `json:"type"`
|
|
Event any `json:"event"`
|
|
}
|
|
|
|
type WsHubSpecificHubEventMessage struct {
|
|
Type WsEventType.WsEventType `json:"type"`
|
|
HubId uuid.UUID `json:"hubId"`
|
|
Event any `json:"event"`
|
|
}
|
|
|
|
type WsAuthMessage struct {
|
|
Success bool `json:"success"`
|
|
Error string `json:"error"`
|
|
}
|
|
|
|
type Permissions uint32
|
|
|
|
const (
|
|
// Hub permissions
|
|
PermissionSetHubName Permissions = 1 << iota
|
|
PermissionSetHubIcon
|
|
PermissionSetHubBg
|
|
PermissionSetHubColor
|
|
PermissionRemoveHub
|
|
PermissionSetUserColorAllowed
|
|
PermissionSetHubJoinRole
|
|
|
|
// User permissions
|
|
PermissionInviteUser
|
|
PermissionRemoveUser
|
|
PermissionRenameUser
|
|
PermissionSelfRename
|
|
PermissionMuteUser
|
|
PermissionUserAddRole
|
|
PermissionUserRemoveRole
|
|
PermissionUserSelfRoleRemove
|
|
|
|
// Role permissions
|
|
PermissionCreateRole
|
|
PermissionRemoveRole
|
|
PermissionSetRoleName
|
|
PermissionSetRoleColor
|
|
PermissionSetRolePermissions
|
|
|
|
// Channel permissions
|
|
PermissionCreateChannel
|
|
PermissionRemoveChannel
|
|
PermissionSetChannelName
|
|
PermissionSetChannelDescription
|
|
PermissionSetChannelIcon
|
|
PermissionSetChannelPermittedVisibleRoles
|
|
PermissionSetChannelPermittedSendMessageRoles
|
|
PermissionSetChannelPermittedReadHistoryRoles
|
|
)
|
|
|
|
var permissionRegistry = map[string]Permissions{
|
|
"set_hub_name": PermissionSetHubName,
|
|
"set_hub_icon": PermissionSetHubIcon,
|
|
"set_hub_bg": PermissionSetHubBg,
|
|
"set_hub_color": PermissionSetHubColor,
|
|
"remove_hub": PermissionRemoveHub,
|
|
"set_user_color_allowed": PermissionSetUserColorAllowed,
|
|
"set_hub_join_role": PermissionSetHubJoinRole,
|
|
"invite_user": PermissionInviteUser,
|
|
"remove_user": PermissionRemoveUser,
|
|
"rename_user": PermissionRenameUser,
|
|
"self_rename": PermissionSelfRename,
|
|
"mute_user": PermissionMuteUser,
|
|
"user_add_role": PermissionUserAddRole,
|
|
"user_remove_role": PermissionUserRemoveRole,
|
|
"create_role": PermissionCreateRole,
|
|
"remove_role": PermissionRemoveRole,
|
|
"set_role_name": PermissionSetRoleName,
|
|
"set_role_color": PermissionSetRoleColor,
|
|
"set_role_permissions": PermissionSetRolePermissions,
|
|
"self_role_remove": PermissionUserSelfRoleRemove,
|
|
"add_channel": PermissionCreateChannel,
|
|
"remove_channel": PermissionRemoveChannel,
|
|
"set_channel_name": PermissionSetChannelName,
|
|
"set_channel_icon": PermissionSetChannelIcon,
|
|
"set_channel_permitted_visible": PermissionSetChannelPermittedVisibleRoles,
|
|
"set_channel_permitted_send": PermissionSetChannelPermittedSendMessageRoles,
|
|
"set_channel_permitted_read": PermissionSetChannelPermittedReadHistoryRoles,
|
|
}
|
|
|
|
func PermissionLookup(permission string) (Permissions, bool) {
|
|
p, ok := permissionRegistry[permission]
|
|
return p, ok
|
|
}
|
|
|
|
func (p *Permissions) Grant(permissions Permissions) {
|
|
*p |= permissions
|
|
}
|
|
func (p *Permissions) GrantMultiple(perms []Permissions) {
|
|
for _, perm := range perms {
|
|
*p |= perm
|
|
}
|
|
}
|
|
func (p *Permissions) Revoke(permissions Permissions) {
|
|
*p &^= permissions
|
|
}
|
|
func (p *Permissions) Has(permissions Permissions) bool {
|
|
return *p&permissions != 0
|
|
}
|
|
|
|
func PermissionAll() Permissions {
|
|
return math.MaxUint32
|
|
}
|
|
|
|
type HubBoundRoles [3]uint64
|
|
|
|
const HubBoundRolesMax uint8 = 191
|
|
|
|
func (r *HubBoundRoles) Add(bit uint8) { r[bit>>6] |= 1 << (bit & 63) }
|
|
func (r *HubBoundRoles) Remove(bit uint8) { r[bit>>6] &^= 1 << (bit & 63) }
|
|
func (r *HubBoundRoles) ContainsRoleId(bit uint8) bool { return r[bit>>6]>>(bit&63)&1 == 1 }
|
|
func (r *HubBoundRoles) DoesIntersect(against HubBoundRoles) bool {
|
|
return against[0]&r[0] != 0 || against[1]&r[1] != 0 || against[2]&r[2] != 0
|
|
}
|
|
|
|
type CachedUserPermissions uint8
|
|
|
|
const (
|
|
CachedUserCanView CachedUserPermissions = 1 << iota
|
|
CachedUserCanReadHistory
|
|
CachedUserCanMessage
|
|
)
|
|
|
|
const CachedUserPermissionsAll = CachedUserCanView | CachedUserCanReadHistory | CachedUserCanMessage
|
|
|
|
func (p *CachedUserPermissions) SetCanView() { *p |= CachedUserCanView }
|
|
func (p *CachedUserPermissions) ClearCanView() { *p &^= CachedUserCanView }
|
|
func (p CachedUserPermissions) CanView() bool { return p&CachedUserCanView != 0 }
|
|
|
|
func (p *CachedUserPermissions) SetCanReadHistory() { *p |= CachedUserCanReadHistory }
|
|
func (p *CachedUserPermissions) ClearCanReadHistory() { *p &^= CachedUserCanReadHistory }
|
|
func (p CachedUserPermissions) CanReadHistory() bool { return p&CachedUserCanReadHistory != 0 }
|
|
|
|
func (p *CachedUserPermissions) SetCanMessage() { *p |= CachedUserCanMessage }
|
|
func (p *CachedUserPermissions) ClearCanMessage() { *p &^= CachedUserCanMessage }
|
|
func (p CachedUserPermissions) CanMessage() bool { return p&CachedUserCanMessage != 0 }
|
|
|
|
type HubUpdate struct {
|
|
Name bool
|
|
IconUrl bool
|
|
BgUrl bool
|
|
JoinRole bool
|
|
Id bool
|
|
Creator bool
|
|
Color bool
|
|
UserColorAllowed bool
|
|
}
|
|
|
|
type Hub struct {
|
|
Mu sync.RWMutex `json:"-"`
|
|
CreatedAt time.Time `json:"createdAt"`
|
|
Roles [256]*HubRole `json:"-"`
|
|
Users map[uuid.UUID]*HubUser `json:"-"`
|
|
Channels map[uuid.UUID]*HubChannel `json:"-"`
|
|
Name string `json:"name"`
|
|
IconUrl string `json:"iconUrl"`
|
|
BgUrl string `json:"backgroundUrl"`
|
|
JoinRole *HubRole `json:"joinRole"`
|
|
Id uuid.UUID `json:"id"`
|
|
Creator uuid.UUID `json:"creator"`
|
|
Color Rgba `json:"color"`
|
|
UserColorAllowed bool `json:"userColorAllowed"`
|
|
}
|
|
|
|
func NewHub() *Hub {
|
|
return &Hub{
|
|
Users: make(map[uuid.UUID]*HubUser),
|
|
Channels: map[uuid.UUID]*HubChannel{},
|
|
}
|
|
}
|
|
|
|
type HubRoleUpdate struct {
|
|
Name bool
|
|
Permissions bool
|
|
Color bool
|
|
Id bool
|
|
}
|
|
|
|
type HubRole struct {
|
|
Name string `json:"role"`
|
|
CreatedAt time.Time `json:"createdAt"`
|
|
Permissions Permissions `json:"permissions"`
|
|
Color Rgba `json:"color"`
|
|
Id uint8 `json:"id"`
|
|
}
|
|
|
|
func (h *HubRole) GrantPermission(r Permissions) {
|
|
h.Permissions |= r
|
|
}
|
|
func (h *HubRole) RevokePermission(r Permissions) {
|
|
h.Permissions &^= r
|
|
}
|
|
func (h *HubRole) HasPermission(r Permissions) bool {
|
|
return h.Permissions&r != 0
|
|
}
|
|
|
|
type HubUserUpdate struct {
|
|
Roles bool
|
|
Name bool
|
|
IsMuted bool
|
|
}
|
|
|
|
type HubUser struct {
|
|
Mu sync.RWMutex `json:"-"`
|
|
CreatedAt time.Time `json:"createdAt"`
|
|
Roles HubBoundRoles `json:"roles"`
|
|
Name string `json:"name"` // Name empty = original name
|
|
OriginalId uuid.UUID `json:"originalId"`
|
|
IsMuted bool `json:"isMuted"`
|
|
}
|
|
|
|
func NewHubUser() *HubUser {
|
|
return &HubUser{}
|
|
}
|
|
|
|
type HubChannelUpdate struct {
|
|
Name bool
|
|
Description bool
|
|
IconUrl bool
|
|
CreatedAt bool
|
|
RolesCanView bool
|
|
RolesCanMessage bool
|
|
RolesCanReadHistory bool
|
|
Position bool
|
|
}
|
|
|
|
type HubChannel struct {
|
|
Mu sync.RWMutex `json:"-"`
|
|
MessagesBuff []*Message `json:"-"`
|
|
Name string `json:"name"`
|
|
Description string `json:"description"`
|
|
IconUrl string `json:"iconUrl"`
|
|
CreatedAt time.Time `json:"createdAt"`
|
|
RolesCanView HubBoundRoles `json:"rolesCanView"`
|
|
RolesCanMessage HubBoundRoles `json:"rolesCanMessage"`
|
|
RolesCanReadHistory HubBoundRoles `json:"rolesCanReadHistory"`
|
|
UsersCachedPermissions map[uuid.UUID]CachedUserPermissions `json:"-"`
|
|
NextBuffIdx uint32 `json:"-"`
|
|
Id uuid.UUID `json:"id"`
|
|
Position uint8 `json:"position"`
|
|
HaveOverflowed bool `json:"-"`
|
|
}
|
|
|
|
func NewHubChannel() *HubChannel {
|
|
return &HubChannel{
|
|
MessagesBuff: make([]*Message, config.MaxHubChannelMsgCache),
|
|
UsersCachedPermissions: make(map[uuid.UUID]CachedUserPermissions),
|
|
}
|
|
}
|
|
func (conn *HubChannel) AddMessageToBuff(message *Message) {
|
|
conn.Mu.Lock()
|
|
defer conn.Mu.Unlock()
|
|
|
|
size := uint32(len(conn.MessagesBuff))
|
|
conn.MessagesBuff[conn.NextBuffIdx%size] = message
|
|
conn.NextBuffIdx++
|
|
if conn.NextBuffIdx >= size {
|
|
conn.HaveOverflowed = true
|
|
}
|
|
}
|
|
|
|
// GetSortedMessagesBuff returns slice and its valid length.
|
|
func (conn *HubChannel) GetSortedMessagesBuff() ([]*Message, uint32) {
|
|
conn.Mu.RLock()
|
|
defer conn.Mu.RUnlock()
|
|
|
|
size := uint32(len(conn.MessagesBuff))
|
|
if !conn.HaveOverflowed {
|
|
return conn.MessagesBuff, conn.NextBuffIdx
|
|
}
|
|
|
|
sorted := make([]*Message, size)
|
|
for i := uint32(0); i < size; i++ {
|
|
sorted[i] = conn.MessagesBuff[(conn.NextBuffIdx+i)%size]
|
|
}
|
|
return sorted, size
|
|
}
|