needed save users that can view channel instead of checking

This commit is contained in:
2026-04-26 15:09:35 +02:00
parent f22bb43346
commit 9c73a01101
8 changed files with 130 additions and 39 deletions
@@ -9,4 +9,5 @@ const (
ConnectionDeleted ConnectionDeleted
ConnectionElevated ConnectionElevated
ConnectionDeElevated ConnectionDeElevated
HubMessage
) )
+2 -2
View File
@@ -20,7 +20,7 @@ var (
MaxRequestWithAvatarBytes uint32 = 1 << 20 MaxRequestWithAvatarBytes uint32 = 1 << 20
MaxRequestWithProfileBgBytes uint32 = 4 << 20 MaxRequestWithProfileBgBytes uint32 = 4 << 20
FileProcessingPartBytes uint64 = 12 << 20 FileProcessingPartBytes uint64 = 12 << 20
FileProcessingThreads uint32 = 3 FileProcessingThreads uint = 3
FileDownloadLinkTtl time.Duration = 24 * time.Hour FileDownloadLinkTtl time.Duration = 24 * time.Hour
) )
@@ -35,7 +35,7 @@ type configFile struct {
MaxRequestWithAvatarBytes uint32 `toml:"max_request_with_avatar_bytes"` MaxRequestWithAvatarBytes uint32 `toml:"max_request_with_avatar_bytes"`
MaxRequestWithProfileBgBytes uint32 `toml:"max_request_with_profile_bg_bytes"` MaxRequestWithProfileBgBytes uint32 `toml:"max_request_with_profile_bg_bytes"`
FileProcessingPartBytes uint64 `toml:"file_processing_part_bytes"` FileProcessingPartBytes uint64 `toml:"file_processing_part_bytes"`
FileProcessingThreads uint32 `toml:"file_processing_threads"` FileProcessingThreads uint `toml:"file_processing_threads"`
FileDownloadLinkTtl time.Duration `toml:"file_download_link_ttl"` FileDownloadLinkTtl time.Duration `toml:"file_download_link_ttl"`
} }
+1 -1
View File
@@ -45,7 +45,7 @@ func HandleAttachmentFileUpload(response http.ResponseWriter, request *http.Requ
key := minio.GetKey(minio.GetKeyOptions{ key := minio.GetKey(minio.GetKeyOptions{
ConnectionId: conn.Id, ConnectionId: conn.Id,
MimeType: contentType, MimeType: contentType,
UploadType: minio.File, UploadType: minio.ConnectionFile,
}) })
if err = minio.Upload(ctx, key, file, header.Size, contentType, map[string]string{ if err = minio.Upload(ctx, key, file, header.Size, contentType, map[string]string{
+40 -5
View File
@@ -3,6 +3,7 @@ package httpRequest
import ( import (
"context" "context"
"errors" "errors"
"log/slog"
"net/http" "net/http"
"go-socket/packages/convertions" "go-socket/packages/convertions"
@@ -57,13 +58,11 @@ func getHubByIdStr(ctx context.Context, hubId string) (*types.Hub, error) {
} }
hub, ok := cache.GetHubById(hubUuid) hub, ok := cache.GetHubById(hubUuid)
if !ok { if !ok {
hub = types.CreateHub() return nil, errors.New("hub not found")
hub.Id = hubUuid
if err := postgresql.GetWholeHub(ctx, hub); err != nil {
return nil, err
}
} }
return hub, nil return hub, nil
} }
@@ -91,3 +90,39 @@ func getHubUserIfValidWithResponseOnFail(ctx context.Context, response http.Resp
return user, hubUser, hub, nil return user, hubUser, hub, nil
} }
func getHubChannelIfValidWithResponseOnFail(ctx context.Context, response http.ResponseWriter, hub *types.Hub, hubUser *types.HubUser, channelId string) (
*types.HubChannel, error) {
channelUuid, err := convertions.StringToUuid(channelId)
if err != nil {
http.Error(response, "invalid channelid", http.StatusBadRequest)
return nil, errors.New("invalid channelid")
}
channel, ok := hub.Channels[channelUuid]
if !ok {
http.Error(response, "invalid channelid", http.StatusUnauthorized)
return nil, errors.New("invalid channelid")
}
group := hub.Groups[channel.ParentId]
if group == nil {
slog.Warn("hub channel has no parent group", "Hub", hub.Id, "Channel", channel.Id)
http.Error(response, "internal server error", http.StatusInternalServerError)
return nil, errors.New("internal server error")
}
if !group.RolesCanView.HasSameId(hubUser.Roles) {
http.Error(response, "invalid channelid", http.StatusUnauthorized)
return nil, errors.New("invalid channelid")
}
if !channel.RolesCanView.HasSameId(hubUser.Roles) {
http.Error(response, "invalid channelid", http.StatusUnauthorized)
return nil, errors.New("invalid channelid")
}
return channel, nil
}
// TODO cache on roles or channels needed for quick lookup
func getHubChannelReadHistorayAndViewChannel(hub *types.Hub, channel *types.HubChannel) []*types.HubUser
+52 -3
View File
@@ -4,11 +4,14 @@ import (
"net/http" "net/http"
"time" "time"
"go-socket/packages/convertions"
"go-socket/packages/types" "go-socket/packages/types"
"github.com/google/uuid" "github.com/google/uuid"
) )
func canHubUserMessage(channel)
func HandleHubCreate(response http.ResponseWriter, request *http.Request) { func HandleHubCreate(response http.ResponseWriter, request *http.Request) {
if !validCheckWithResponseOnFail(&response, request, normal) { if !validCheckWithResponseOnFail(&response, request, normal) {
return return
@@ -21,7 +24,7 @@ func HandleHubCreate(response http.ResponseWriter, request *http.Request) {
return return
} }
hubName := request.Header.Get("hubname") hubName := request.FormValue("hubname")
if hubName == "" { if hubName == "" {
http.Error(response, "hub name is required", http.StatusBadRequest) http.Error(response, "hub name is required", http.StatusBadRequest)
return return
@@ -35,9 +38,9 @@ func HandleHubCreate(response http.ResponseWriter, request *http.Request) {
hub.CreatedAt = time.Now() hub.CreatedAt = time.Now()
creator := types.NewHubUser() creator := types.NewHubUser()
creator.Name = user.Name
creator.OriginalId = user.Id creator.OriginalId = user.Id
creator.CreatedAt = hub.CreatedAt creator.CreatedAt = hub.CreatedAt
hub.Users[creator.OriginalId] = creator
rootRole := &types.HubRole{ rootRole := &types.HubRole{
Id: uint8(0), Id: uint8(0),
@@ -50,7 +53,7 @@ func HandleHubCreate(response http.ResponseWriter, request *http.Request) {
creator.Roles.Add(rootRole.Id) creator.Roles.Add(rootRole.Id)
memberRole := &types.HubRole{ memberRole := &types.HubRole{
Id: uint8(255), Id: types.BoundRolesMax,
Name: "member", Name: "member",
Color: types.Rgba{}.GetRandom(), Color: types.Rgba{}.GetRandom(),
CreatedAt: hub.CreatedAt, CreatedAt: hub.CreatedAt,
@@ -63,6 +66,8 @@ func HandleHubCreate(response http.ResponseWriter, request *http.Request) {
rootGroup.Id = uint8(1) rootGroup.Id = uint8(1)
rootGroup.Color = types.Rgba{}.GetRandom() rootGroup.Color = types.Rgba{}.GetRandom()
rootGroup.CreatedAt = hub.CreatedAt rootGroup.CreatedAt = hub.CreatedAt
rootGroup.RolesCanView.Add(rootRole.Id)
rootGroup.RolesCanView.Add(memberRole.Id)
hub.Groups[rootGroup.Id] = rootGroup hub.Groups[rootGroup.Id] = rootGroup
channel := types.NewHubChannel() channel := types.NewHubChannel()
@@ -71,5 +76,49 @@ func HandleHubCreate(response http.ResponseWriter, request *http.Request) {
channel.Id = uuid.New() channel.Id = uuid.New()
channel.ParentId = rootGroup.Id channel.ParentId = rootGroup.Id
channel.Description = "The fist channel!" channel.Description = "The fist channel!"
channel.CreatedAt = hub.CreatedAt
channel.RolesCanMessage.Add(rootGroup.Id)
channel.RolesCanMessage.Add(memberRole.Id)
channel.RolesCanView.Add(rootGroup.Id)
channel.RolesCanView.Add(memberRole.Id)
channel.RolesCanReadHistory.Add(rootGroup.Id)
channel.RolesCanReadHistory.Add(memberRole.Id)
hub.Channels[channel.Id] = channel hub.Channels[channel.Id] = channel
} }
func HandleChannelSendMessage(response http.ResponseWriter, request *http.Request) {
if !validCheckWithResponseOnFail(&response, request, normal) {
return
}
ctx := request.Context()
user, hubUser, hub, err := getHubUserIfValidWithResponseOnFail(ctx, response, request.Header.Get("token"), request.FormValue("hubid"))
if err != nil {
return
}
channelId, err := convertions.StringToUuid(request.FormValue("channelid"))
if err != nil {
http.Error(response, "invalid channelid", http.StatusBadRequest)
return
}
msgContent := request.FormValue("msgContent")
attachedFile := request.FormValue("attachedFile")
if msgContent == "" && attachedFile == "" {
http.Error(response, "empty msgContent", http.StatusBadRequest)
return
}
// TODO add check in future
// if attachedFile != "" && !strings.HasPrefix(attachedFile, conn.Id.String()+"/") {
// http.Error(response, "invalid attachedFile", http.StatusBadRequest)
// return
// }
channel, err := getHubChannelIfValidWithResponseOnFail(ctx, response, hub, hubUser, request.FormValue("hubid"))
if err != nil {
return
}
}
+10 -5
View File
@@ -20,7 +20,8 @@ var minClient *minio.Client
type DataType uint8 type DataType uint8
const ( const (
File DataType = iota ConnectionFile DataType = iota
HubChannelFile
UserAvatar UserAvatar
UserProfileBg UserProfileBg
) )
@@ -28,14 +29,16 @@ const (
type DataTypePrefix string type DataTypePrefix string
const ( const (
FilePrefix DataTypePrefix = "upload/" ConnectionFilePrefix DataTypePrefix = "connection/"
UserAvatarPrefix DataTypePrefix = "userAvatar/" HubChannelFilePrefix DataTypePrefix = "hub/"
UserProfileBgPrefix DataTypePrefix = "userProfileBg/" UserAvatarPrefix DataTypePrefix = "userAvatar/"
UserProfileBgPrefix DataTypePrefix = "userProfileBg/"
) )
type GetKeyOptions struct { type GetKeyOptions struct {
UserId uuid.UUID UserId uuid.UUID
ConnectionId uuid.UUID ConnectionId uuid.UUID
ChannelId uuid.UUID
MimeType string MimeType string
UploadType DataType UploadType DataType
} }
@@ -49,12 +52,14 @@ func GetKey(opts GetKeyOptions) string {
key := "/" + strconv.FormatInt(time.Now().UnixMilli(), 10) + extensions[0] key := "/" + strconv.FormatInt(time.Now().UnixMilli(), 10) + extensions[0]
switch opts.UploadType { switch opts.UploadType {
case HubChannelFile:
return string(HubChannelFilePrefix) + opts.ChannelId.String() + key
case UserAvatar: case UserAvatar:
return string(UserAvatarPrefix) + opts.UserId.String() + key return string(UserAvatarPrefix) + opts.UserId.String() + key
case UserProfileBg: case UserProfileBg:
return string(UserProfileBgPrefix) + opts.UserId.String() + key return string(UserProfileBgPrefix) + opts.UserId.String() + key
default: default:
return string(FilePrefix) + opts.ConnectionId.String() + key return string(ConnectionFilePrefix) + opts.ConnectionId.String() + key
} }
} }
+23 -23
View File
@@ -187,23 +187,22 @@ func PermissionAll() Permissions {
return math.MaxUint32 return math.MaxUint32
} }
type BoundRoles uint64 type BoundRoles [3]uint64
func (b *BoundRoles) Add(id uint8) { const BoundRolesMax uint8 = 191
*b |= 1 << id
} func (r *BoundRoles) Add(bit uint8) { r[bit>>6] |= 1 << (bit & 63) }
func (b *BoundRoles) Remove(id uint8) { func (r *BoundRoles) Remove(bit uint8) { r[bit>>6] &^= 1 << (bit & 63) }
*b &^= 1 << id func (r *BoundRoles) Has(bit uint8) bool { return r[bit>>6]>>(bit&63)&1 == 1 }
} func (r *BoundRoles) HasSameId(against BoundRoles) bool {
func (b *BoundRoles) Has(id uint8) bool { return against[0]&r[0] != 0 || against[1]&r[1] != 0 || against[2]&r[2] != 0
return *b&(1<<id) != 0
} }
type Hub struct { type Hub struct {
Mu sync.RWMutex `json:"-"` Mu sync.RWMutex `json:"-"`
CreatedAt time.Time `json:"createdAt"` CreatedAt time.Time `json:"createdAt"`
Roles [256]*HubRole `json:"-"` Roles [256]*HubRole `json:"-"`
Users [256]*HubUser `json:"-"` Users map[uuid.UUID]*HubUser `json:"-"`
Groups [256]*HubGroup `json:"-"` Groups [256]*HubGroup `json:"-"`
Channels map[uuid.UUID]*HubChannel `json:"-"` Channels map[uuid.UUID]*HubChannel `json:"-"`
Name string `json:"name"` Name string `json:"name"`
@@ -218,6 +217,7 @@ type Hub struct {
func NewHub() *Hub { func NewHub() *Hub {
return &Hub{ return &Hub{
Users: make(map[uuid.UUID]*HubUser),
Channels: make(map[uuid.UUID]*HubChannel), Channels: make(map[uuid.UUID]*HubChannel),
} }
} }
@@ -241,6 +241,19 @@ func (h *HubRole) HasPermission(r Permissions) bool {
return h.Permissions&r != 0 return h.Permissions&r != 0
} }
type HubUser struct {
Mu sync.RWMutex `json:"mu"`
CreatedAt time.Time `json:"createdAt"`
Roles BoundRoles `json:"-"`
Name string `json:"name"` // Name empty = original name
OriginalId uuid.UUID `json:"originalId"`
IsMuted bool `json:"isMuted"`
}
func NewHubUser() *HubUser {
return &HubUser{}
}
type HubGroup struct { type HubGroup struct {
Name string `json:"name"` Name string `json:"name"`
CreatedAt time.Time `json:"createdAt"` CreatedAt time.Time `json:"createdAt"`
@@ -253,19 +266,6 @@ func NewHubGroup() *HubGroup {
return &HubGroup{} return &HubGroup{}
} }
type HubUser struct {
Mu sync.RWMutex `json:"mu"`
CreatedAt time.Time `json:"createdAt"`
Roles BoundRoles `json:"-"`
Name string `json:"name"`
OriginalId uuid.UUID `json:"originalId"`
IsMuted bool `json:"isMuted"`
}
func NewHubUser() *HubUser {
return &HubUser{}
}
type HubChannel struct { type HubChannel struct {
Mu sync.RWMutex `json:"-"` Mu sync.RWMutex `json:"-"`
MessagesBuff []*Message `json:"-"` MessagesBuff []*Message `json:"-"`
+1
View File
@@ -1,6 +1,7 @@
when user not ws connected collect count of unread messages for each conn (add db table in future) when user not ws connected collect count of unread messages for each conn (add db table in future)
add hubs add hubs
setting avatar and profilebg
check when mutex needed check when mutex needed
user banners user banners