Files
go-socket/packages/types/types.go
T
admin c0d4483154 fix hub bugs, add channel role permission endpoints
Covers: snake_case param renames, mutex RLock/Unlock mismatches, user.Hubs keyed by hub.Id, hub color using new_name, DELETE params sent as query, delete(target.Hubs, hub.Id), root/member role Id swap, and the three new PATCH
  /hub/channel/roles/* handlers.
2026-05-03 15:51:57 +02:00

372 lines
12 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:"-"`
Color Rgba `json:"color"`
}
type UserProfileUpdateList 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 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
// 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,
"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 = CachedUserCanMessage | CachedUserCanReadHistory | CachedUserCanReadHistory
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 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 HubRole struct {
Name string `json:"role"`
CreatedAt time.Time `json:"createdAt"`
Permissions Permissions `json:"permissions"`
Color Rgba `json:"color"`
Id uint8 `json:"id"`
BoundedGroup uint8 `json:"boundedGroup"` // BoundedGroup 0 for global
}
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 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 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
}