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.
This commit is contained in:
2026-05-03 15:51:57 +02:00
parent cb003d235f
commit c0d4483154
9 changed files with 732 additions and 218 deletions
+9 -9
View File
@@ -33,21 +33,21 @@ func HandleDm(response http.ResponseWriter, request *http.Request) {
return
}
conn, ok := getConnectionWithResponseOnFail(response, request.FormValue("connectionid"), user)
conn, ok := getConnectionWithResponseOnFail(response, request.FormValue("connection_id"), user)
if !ok {
return
}
msgContent := request.FormValue("msgContent")
attachedFile := request.FormValue("attachedFile")
msgContent := request.FormValue("msg_content")
attachedFile := request.FormValue("attached_file")
if msgContent == "" && attachedFile == "" {
http.Error(response, "empty msgContent", http.StatusBadRequest)
http.Error(response, "empty msg_content", http.StatusBadRequest)
return
}
if attachedFile != "" && !strings.HasPrefix(attachedFile, string(minio.ConnectionFilePrefix)+conn.Id.String()+"/") {
http.Error(response, "invalid attachedFile", http.StatusBadRequest)
http.Error(response, "invalid attached_file", http.StatusBadRequest)
return
}
@@ -140,7 +140,7 @@ func HandleUserGetConnectionMessages(response http.ResponseWriter, request *http
return
}
conn, ok := getConnectionWithResponseOnFail(response, request.FormValue("connectionid"), user)
conn, ok := getConnectionWithResponseOnFail(response, request.FormValue("connection_id"), user)
if !ok {
return
}
@@ -268,7 +268,7 @@ func HandleUserDeleteConnection(response http.ResponseWriter, request *http.Requ
return
}
conn, ok := getConnectionWithResponseOnFail(response, request.FormValue("connectionid"), user)
conn, ok := getConnectionWithResponseOnFail(response, request.FormValue("connection_id"), user)
if !ok {
return
}
@@ -305,7 +305,7 @@ func HandleUserElevateConnection(response http.ResponseWriter, request *http.Req
http.Error(response, "invalid token", http.StatusUnauthorized)
return
}
conn, ok := getConnectionWithResponseOnFail(response, request.FormValue("connectionid"), user)
conn, ok := getConnectionWithResponseOnFail(response, request.FormValue("connection_id"), user)
if !ok {
return
}
@@ -374,7 +374,7 @@ func HandleUserDeElevateConnection(response http.ResponseWriter, request *http.R
return
}
conn, ok := getConnectionWithResponseOnFail(response, request.FormValue("connectionid"), user)
conn, ok := getConnectionWithResponseOnFail(response, request.FormValue("connection_id"), user)
if !ok {
return
}
+4 -4
View File
@@ -153,9 +153,9 @@ func HandleGetUserAvatar(response http.ResponseWriter, request *http.Request) {
return
}
targetId, err := convertions.StringToUuid(request.URL.Query().Get("userid"))
targetId, err := convertions.StringToUuid(request.URL.Query().Get("user_id"))
if err != nil {
http.Error(response, "invalid userid", http.StatusBadRequest)
http.Error(response, "invalid user_id", http.StatusBadRequest)
return
}
@@ -274,9 +274,9 @@ func HandleGetUserProfileBg(response http.ResponseWriter, request *http.Request)
return
}
targetId, err := convertions.StringToUuid(request.URL.Query().Get("userid"))
targetId, err := convertions.StringToUuid(request.URL.Query().Get("user_id"))
if err != nil {
http.Error(response, "invalid userid", http.StatusBadRequest)
http.Error(response, "invalid user_id", http.StatusBadRequest)
return
}
+11 -8
View File
@@ -73,9 +73,9 @@ func getHubUserIfValidWithResponseOnFail(ctx context.Context, response http.Resp
return nil, nil, nil, errors.New("invalid token")
}
hub, err := getHubByIdStr(ctx, request.Header.Get("hubid"))
hub, err := getHubByIdStr(ctx, request.Header.Get("hub_id"))
if err != nil {
http.Error(response, "invalid hubid", http.StatusBadRequest)
http.Error(response, "invalid hub_id", http.StatusBadRequest)
return nil, nil, nil, errors.New("no such hub")
}
@@ -83,8 +83,8 @@ func getHubUserIfValidWithResponseOnFail(ctx context.Context, response http.Resp
hubUser, ok := hub.Users[user.Id]
hub.Mu.RUnlock()
if !ok {
http.Error(response, "invalid hubid", http.StatusUnauthorized)
return nil, nil, nil, errors.New("invalid hubid")
http.Error(response, "invalid hub_id", http.StatusUnauthorized)
return nil, nil, nil, errors.New("invalid hub_id")
}
return user, hubUser, hub, nil
@@ -94,13 +94,16 @@ func getHubChannelIfValidWithResponseOnFail(ctx context.Context, response http.R
*types.HubChannel, error) {
channelUuid, err := convertions.StringToUuid(channelId)
if err != nil {
http.Error(response, "invalid channelid", http.StatusBadRequest)
return nil, errors.New("invalid channelid")
http.Error(response, "invalid channel_id", http.StatusBadRequest)
return nil, errors.New("invalid channel_id")
}
hub.Mu.RLock()
channel, ok := hub.Channels[channelUuid]
hub.Mu.RUnlock()
if !ok {
http.Error(response, "invalid channelid", http.StatusBadRequest)
return nil, errors.New("invalid channelid")
http.Error(response, "channel not found", http.StatusNotFound)
return nil, errors.New("channel not found")
}
if !haveHubUserCachedPermissions(types.CachedUserCanView, hubUser, channel) {
+298 -159
View File
@@ -3,9 +3,7 @@ package httpRequest
import (
"context"
"encoding/json"
"maps"
"net/http"
"slices"
"strings"
"time"
@@ -57,25 +55,24 @@ func hubUserHighestRoleId(u *types.HubUser, h *types.Hub) (id uint8) {
func updateChannelCacheForSpecUserAndChannel(u *types.HubUser, c *types.HubChannel) {
c.Mu.Lock()
defer c.Mu.Unlock()
if u == nil {
delete(c.UsersCachedPermissions, c.Id)
return
}
if u.Roles.DoesIntersect(c.RolesCanView) {
c.UsersCachedPermissions[u.OriginalId] |= types.CachedUserCanView
} else {
if u.Roles.DoesIntersect(c.RolesCanView) {
c.UsersCachedPermissions[u.OriginalId] |= types.CachedUserCanView
} else {
c.UsersCachedPermissions[u.OriginalId] &^= types.CachedUserCanView
}
if u.Roles.DoesIntersect(c.RolesCanReadHistory) {
c.UsersCachedPermissions[u.OriginalId] |= types.CachedUserCanReadHistory
} else {
c.UsersCachedPermissions[u.OriginalId] &^= types.CachedUserCanReadHistory
}
if u.Roles.DoesIntersect(c.RolesCanMessage) {
c.UsersCachedPermissions[u.OriginalId] |= types.CachedUserCanMessage
} else {
c.UsersCachedPermissions[u.OriginalId] &^= types.CachedUserCanMessage
}
c.UsersCachedPermissions[u.OriginalId] &^= types.CachedUserCanView
}
if u.Roles.DoesIntersect(c.RolesCanReadHistory) {
c.UsersCachedPermissions[u.OriginalId] |= types.CachedUserCanReadHistory
} else {
c.UsersCachedPermissions[u.OriginalId] &^= types.CachedUserCanReadHistory
}
if u.Roles.DoesIntersect(c.RolesCanMessage) {
c.UsersCachedPermissions[u.OriginalId] |= types.CachedUserCanMessage
} else {
c.UsersCachedPermissions[u.OriginalId] &^= types.CachedUserCanMessage
}
}
@@ -91,17 +88,23 @@ func updateChannelCacheForSpecUser(u *types.HubUser, h *types.Hub) {
h.Mu.RLock()
defer h.Mu.RUnlock()
for _, c := range h.Channels {
if c == nil {
continue
}
updateChannelCacheForSpecUserAndChannel(u, c)
}
}
func updateChannelCacheForSpecRole(r *types.HubRole, h *types.Hub) {
h.Mu.Lock()
defer h.Mu.Unlock()
h.Mu.RLock()
var users []*types.HubUser
for _, u := range h.Users {
if !u.Roles.ContainsRoleId(r.Id) {
continue
if u.Roles.ContainsRoleId(r.Id) {
users = append(users, u)
}
}
h.Mu.RUnlock()
for _, u := range users {
updateChannelCacheForSpecUser(u, h)
}
}
@@ -128,7 +131,7 @@ func HandleHubCreate(response http.ResponseWriter, request *http.Request) {
return
}
hubName := request.FormValue("hubname")
hubName := request.FormValue("hub_name")
if hubName == "" {
http.Error(response, "hub name is required", http.StatusBadRequest)
return
@@ -140,7 +143,7 @@ func HandleHubCreate(response http.ResponseWriter, request *http.Request) {
hub.Id = uuid.New()
hub.Creator = user.Id
hub.CreatedAt = time.Now()
user.Hubs[user.Id] = hub
user.Hubs[hub.Id] = hub
creator := types.NewHubUser()
creator.OriginalId = user.Id
@@ -148,7 +151,7 @@ func HandleHubCreate(response http.ResponseWriter, request *http.Request) {
hub.Users[creator.OriginalId] = creator
rootRole := &types.HubRole{
Id: uint8(0),
Id: types.HubBoundRolesMax,
Permissions: types.PermissionAll(),
Name: "root",
Color: types.RandomRgba(),
@@ -158,7 +161,7 @@ func HandleHubCreate(response http.ResponseWriter, request *http.Request) {
creator.Roles.Add(rootRole.Id)
memberRole := &types.HubRole{
Id: types.HubBoundRolesMax,
Id: uint8(0),
Name: "member",
Color: types.RandomRgba(),
CreatedAt: hub.CreatedAt,
@@ -179,9 +182,13 @@ func HandleHubCreate(response http.ResponseWriter, request *http.Request) {
channel.RolesCanReadHistory.Add(rootRole.Id)
channel.RolesCanReadHistory.Add(memberRole.Id)
channel.UsersCachedPermissions[creator.OriginalId] = types.CachedUserPermissionsAll
hub.Channels[0] = channel
channel.Position = 0
hub.Channels[channel.Id] = channel
cache.SaveHub(hub)
response.WriteHeader(http.StatusCreated)
response.Write([]byte(hub.Id.String()))
}
func HandleHubJoin(response http.ResponseWriter, request *http.Request) {
@@ -194,12 +201,19 @@ func HandleHubJoin(response http.ResponseWriter, request *http.Request) {
http.Error(response, "invalid token", http.StatusUnauthorized)
return
}
hub, err := getHubByIdStr(ctx, request.Header.Get("hubid"))
hub, err := getHubByIdStr(ctx, request.Header.Get("hub_id"))
if err != nil {
http.Error(response, "invalid hubid", http.StatusBadRequest)
http.Error(response, "invalid hub_id", http.StatusBadRequest)
return
}
_, ok := hub.Users[user.Id]
if ok {
http.Error(response, "hub already joined", http.StatusBadRequest)
return
}
user.Hubs[hub.Id] = hub
hubUser := types.NewHubUser()
hubUser.OriginalId = user.Id
if hub.JoinRole != nil {
@@ -208,6 +222,8 @@ func HandleHubJoin(response http.ResponseWriter, request *http.Request) {
hubUser.CreatedAt = time.Now()
hub.Users[hubUser.OriginalId] = hubUser
updateChannelCacheForSpecUser(hubUser, hub)
response.WriteHeader(http.StatusCreated)
}
func HandleHubMessage(response http.ResponseWriter, request *http.Request) {
@@ -219,7 +235,7 @@ func HandleHubMessage(response http.ResponseWriter, request *http.Request) {
if err != nil {
return
}
channel, err := getHubChannelIfValidWithResponseOnFail(ctx, response, hub, hubUser, request.Header.Get("channelid"))
channel, err := getHubChannelIfValidWithResponseOnFail(ctx, response, hub, hubUser, request.Header.Get("channel_id"))
if err != nil {
return
}
@@ -229,8 +245,8 @@ func HandleHubMessage(response http.ResponseWriter, request *http.Request) {
return
}
msgContent := request.FormValue("msgContent")
attachedFile := request.FormValue("attachedFile")
msgContent := request.FormValue("msg_content")
attachedFile := request.FormValue("attached_file")
if msgContent == "" && attachedFile == "" {
http.Error(response, "empty msgContent", http.StatusBadRequest)
@@ -266,69 +282,10 @@ func HandleHubMessage(response http.ResponseWriter, request *http.Request) {
}
wsServer.WsSendMessageCloseIfTimeout(target, msg)
}
}
func HandleGetHubs(response http.ResponseWriter, request *http.Request) {
if !validCheckWithResponseOnFail(response, request, normal) {
return
}
ctx := request.Context()
user, err := getUserByToken(ctx, request.Header.Get("token"))
if err != nil {
http.Error(response, "invalid token", http.StatusBadRequest)
return
}
user.Mu.RLock()
hubs := slices.Collect(maps.Values(user.Hubs))
user.Mu.RUnlock()
if len(hubs) == 0 {
response.WriteHeader(http.StatusNoContent)
response.Write([]byte("no hubs found"))
return
}
converted, err := json.Marshal(hubs)
if err != nil {
http.Error(response, "json error", http.StatusInternalServerError)
return
}
response.WriteHeader(http.StatusOK)
response.Write(converted)
response.WriteHeader(http.StatusCreated)
}
func HandleGetChannels(response http.ResponseWriter, request *http.Request) {
if !validCheckWithResponseOnFail(response, request, normal) {
return
}
ctx := request.Context()
_, _, hub, err := getHubUserIfValidWithResponseOnFail(ctx, response, request)
if err != nil {
return
}
channelMap := make(map[uint8]*types.HubChannel)
hub.Mu.RLock()
for i, channel := range hub.Channels {
if channel == nil {
continue
}
channelMap[uint8(i)] = channel
}
hub.Mu.Unlock()
if len(channelMap) == 0 {
response.WriteHeader(http.StatusNoContent)
return
}
channels, err := json.Marshal(channelMap)
if err != nil {
http.Error(response, "json error", http.StatusInternalServerError)
return
}
response.WriteHeader(http.StatusOK)
response.Write(channels)
}
func HandleGetHubUsers(response http.ResponseWriter, request *http.Request) {
if !validCheckWithResponseOnFail(response, request, normal) {
return
}
@@ -337,27 +294,129 @@ func HandleGetHubUsers(response http.ResponseWriter, request *http.Request) {
if err != nil {
return
}
users := make([]*types.HubUser, 0)
channels := make([]uuid.UUID, 0)
hub.Mu.RLock()
for userId, user := range hub.Users {
if userId == requestor.OriginalId {
for _, channel := range hub.Channels {
if channel == nil || !haveHubUserCachedPermissions(types.CachedUserCanView, requestor, channel) {
continue
}
users = append(users, user)
channels = append(channels, channel.Id)
}
hub.Mu.Unlock()
if len(users) == 0 {
hub.Mu.RUnlock()
if len(channels) == 0 {
response.WriteHeader(http.StatusNoContent)
return
}
channels, err := json.Marshal(users)
marshal, err := json.Marshal(channels)
if err != nil {
http.Error(response, "json error", http.StatusInternalServerError)
return
}
response.WriteHeader(http.StatusOK)
response.Write(channels)
response.Write(marshal)
}
func HandleGetHubUsers(response http.ResponseWriter, request *http.Request) {
if !validCheckWithResponseOnFail(response, request, normal) {
return
}
ctx := request.Context()
_, _, hub, err := getHubUserIfValidWithResponseOnFail(ctx, response, request)
if err != nil {
return
}
users := make([]*types.HubUser, 0)
hub.Mu.RLock()
for _, user := range hub.Users {
users = append(users, user)
}
hub.Mu.RUnlock()
if len(users) == 0 {
response.WriteHeader(http.StatusNoContent)
return
}
marshal, err := json.Marshal(users)
if err != nil {
http.Error(response, "json error", http.StatusInternalServerError)
return
}
response.WriteHeader(http.StatusOK)
response.Write(marshal)
}
func GetRoles(response http.ResponseWriter, request *http.Request) {
if !validCheckWithResponseOnFail(response, request, normal) {
return
}
ctx := request.Context()
_, _, hub, err := getHubUserIfValidWithResponseOnFail(ctx, response, request)
if err != nil {
return
}
hub.Mu.RLock()
roles := make([]*types.HubRole, 0)
for _, role := range hub.Roles {
if role == nil {
continue
}
roles = append(roles, role)
}
hub.Mu.RUnlock()
marshal, err := json.Marshal(roles)
if err != nil {
http.Error(response, "json error", http.StatusInternalServerError)
return
}
response.WriteHeader(http.StatusOK)
response.Write(marshal)
}
func GetHubData(response http.ResponseWriter, request *http.Request) {
if !validCheckWithResponseOnFail(response, request, normal) {
return
}
ctx := request.Context()
_, _, hub, err := getHubUserIfValidWithResponseOnFail(ctx, response, request)
if err != nil {
return
}
marshal, err := json.Marshal(hub)
if err != nil {
http.Error(response, "json error", http.StatusInternalServerError)
}
response.WriteHeader(http.StatusOK)
response.Write(marshal)
}
func GetChannelData(response http.ResponseWriter, request *http.Request) {
if !validCheckWithResponseOnFail(response, request, normal) {
return
}
ctx := request.Context()
_, _, hub, err := getHubUserIfValidWithResponseOnFail(ctx, response, request)
if err != nil {
return
}
channelId, err := convertions.StringToUuid(request.FormValue("channel_id"))
if err != nil {
http.Error(response, "invalid channel_id", http.StatusBadRequest)
return
}
hub.Mu.RLock()
channel, ok := hub.Channels[channelId]
hub.Mu.RUnlock()
if !ok {
http.Error(response, "no such channel", http.StatusNotFound)
return
}
marshal, err := json.Marshal(channel)
if err != nil {
http.Error(response, "json error", http.StatusInternalServerError)
return
}
response.WriteHeader(http.StatusOK)
response.Write(marshal)
}
func hubPermissionContext(response http.ResponseWriter, request *http.Request, rt bodyLimit, needed types.Permissions) (*types.HubUser, *types.Hub, context.Context, uint8, bool) {
@@ -384,7 +443,7 @@ func HandleHubSetName(response http.ResponseWriter, request *http.Request) {
if !ok {
return
}
newName := request.FormValue("newname")
newName := request.FormValue("new_name")
if newName == "" {
http.Error(response, "empty name", http.StatusBadRequest)
return
@@ -398,7 +457,7 @@ func HandleHubSetColor(response http.ResponseWriter, request *http.Request) {
if !ok {
return
}
color, err := convertions.StringToRgba(request.FormValue("newname"))
color, err := convertions.StringToRgba(request.FormValue("new_color"))
if err != nil {
http.Error(response, "bad color", http.StatusBadRequest)
return
@@ -430,28 +489,37 @@ func HandleHubUserRemove(response http.ResponseWriter, request *http.Request) {
if !ok {
return
}
targetId, err := convertions.StringToUuid(request.FormValue("targetid"))
targetId, err := convertions.StringToUuid(request.FormValue("target_id"))
if err != nil {
http.Error(response, "bad targetid", http.StatusBadRequest)
http.Error(response, "bad target_id", http.StatusBadRequest)
return
}
hub.Mu.RLock()
target, exists := hub.Users[targetId]
hubTarget, exists := hub.Users[targetId]
hub.Mu.RUnlock()
if !exists {
hub.Mu.Unlock()
http.Error(response, "user not found", http.StatusNotFound)
return
}
hub.Mu.RUnlock()
if usedRoleId <= hubUserHighestRoleId(target, hub) {
if usedRoleId <= hubUserHighestRoleId(hubTarget, hub) {
http.Error(response, "target higher in hierarchy", http.StatusForbidden)
return
}
target, err := cache.GetUserById(targetId)
if err != nil {
http.Error(response, "target not found", http.StatusInternalServerError)
return
}
target.Mu.Lock()
delete(target.Hubs, hub.Id)
target.Mu.Unlock()
hub.Mu.Lock()
delete(hub.Users, targetId)
updateChannelCacheForSpecUser(target, hub)
hub.Mu.Unlock()
updateChannelCacheForSpecUser(hubTarget, hub)
response.WriteHeader(http.StatusAccepted)
}
@@ -460,27 +528,25 @@ func HandleHubRenameUser(response http.ResponseWriter, request *http.Request) {
if !ok {
return
}
newName := request.FormValue("newname")
targetId, err := convertions.StringToUuid(request.FormValue("targetid"))
newName := request.FormValue("new_name")
targetId, err := convertions.StringToUuid(request.FormValue("target_id"))
if err != nil {
http.Error(response, "bad targetid", http.StatusBadRequest)
http.Error(response, "bad target_id", http.StatusBadRequest)
return
}
hub.Mu.RLock()
target, exists := hub.Users[targetId]
hub.Mu.RUnlock()
if !exists {
hub.Mu.Unlock()
http.Error(response, "user not found", http.StatusNotFound)
return
}
hub.Mu.RUnlock()
if usedRoleId <= hubUserHighestRoleId(target, hub) {
http.Error(response, "target higher in hierarchy", http.StatusForbidden)
return
}
target.Name = newName
response.WriteHeader(http.StatusAccepted)
}
@@ -489,7 +555,7 @@ func HandleHubRenameSelf(response http.ResponseWriter, request *http.Request) {
if !ok {
return
}
requestor.Name = request.FormValue("newname")
requestor.Name = request.FormValue("new_name")
response.WriteHeader(http.StatusAccepted)
}
@@ -498,25 +564,23 @@ func HandleHubToggleMuteUser(response http.ResponseWriter, request *http.Request
if !ok {
return
}
targetId, err := convertions.StringToUuid(request.FormValue("targetid"))
targetId, err := convertions.StringToUuid(request.FormValue("target_id"))
if err != nil {
http.Error(response, "bad targetid", http.StatusBadRequest)
http.Error(response, "bad target_id", http.StatusBadRequest)
return
}
hub.Mu.RLock()
target, exists := hub.Users[targetId]
hub.Mu.RUnlock()
if !exists {
hub.Mu.Unlock()
http.Error(response, "user not found", http.StatusNotFound)
return
}
hub.Mu.RUnlock()
if usedRoleId <= hubUserHighestRoleId(target, hub) {
http.Error(response, "target higher in hierarchy", http.StatusForbidden)
return
}
hub.Mu.RLock()
target.IsMuted = !target.IsMuted
response.WriteHeader(http.StatusAccepted)
}
@@ -526,14 +590,14 @@ func HandleHubUserAddRole(response http.ResponseWriter, request *http.Request) {
if !ok {
return
}
targetId, err := convertions.StringToUuid(request.FormValue("targetid"))
targetId, err := convertions.StringToUuid(request.FormValue("target_id"))
if err != nil {
http.Error(response, "bad targetid", http.StatusBadRequest)
http.Error(response, "bad target_id", http.StatusBadRequest)
return
}
roleId, err := convertions.StringToUint8(request.FormValue("roleid"))
roleId, err := convertions.StringToUint8(request.FormValue("role_id"))
if err != nil {
http.Error(response, "bad roleid", http.StatusBadRequest)
http.Error(response, "bad role_id", http.StatusBadRequest)
return
}
if roleId > usedRoleId {
@@ -563,14 +627,14 @@ func HandleHubUserRemoveRole(response http.ResponseWriter, request *http.Request
if !ok {
return
}
targetId, err := convertions.StringToUuid(request.FormValue("targetid"))
targetId, err := convertions.StringToUuid(request.FormValue("target_id"))
if err != nil {
http.Error(response, "bad targetid", http.StatusBadRequest)
http.Error(response, "bad target_id", http.StatusBadRequest)
return
}
roleId, err := convertions.StringToUint8(request.FormValue("roleid"))
roleId, err := convertions.StringToUint8(request.FormValue("role_id"))
if err != nil {
http.Error(response, "bad roleid", http.StatusBadRequest)
http.Error(response, "bad role_id", http.StatusBadRequest)
return
}
if roleId > usedRoleId {
@@ -630,9 +694,9 @@ func HandleHubRemoveRole(response http.ResponseWriter, request *http.Request) {
if !ok {
return
}
targetRoleId, err := convertions.StringToUint8(request.FormValue("roleid"))
targetRoleId, err := convertions.StringToUint8(request.FormValue("role_id"))
if err != nil {
http.Error(response, "bad roleid", http.StatusBadRequest)
http.Error(response, "bad role_id", http.StatusBadRequest)
return
}
if targetRoleId == 0 {
@@ -660,16 +724,16 @@ func HandleRoleSetName(response http.ResponseWriter, request *http.Request) {
if !ok {
return
}
roleId, err := convertions.StringToUint8(request.FormValue("roleid"))
roleId, err := convertions.StringToUint8(request.FormValue("role_id"))
if err != nil {
http.Error(response, "bad roleid", http.StatusBadRequest)
http.Error(response, "bad role_id", http.StatusBadRequest)
return
}
if roleId > usedRoleId {
http.Error(response, "target role higher in hierarchy", http.StatusForbidden)
return
}
newName := request.FormValue("newname")
newName := request.FormValue("new_name")
if newName == "" {
http.Error(response, "name empty", http.StatusBadRequest)
return
@@ -678,6 +742,7 @@ func HandleRoleSetName(response http.ResponseWriter, request *http.Request) {
hub.Mu.Lock()
role := hub.Roles[roleId]
if role == nil {
hub.Mu.Unlock()
http.Error(response, "no such role", http.StatusNotFound)
return
}
@@ -691,16 +756,16 @@ func HandleRoleSetColor(response http.ResponseWriter, request *http.Request) {
if !ok {
return
}
roleId, err := convertions.StringToUint8(request.FormValue("roleid"))
roleId, err := convertions.StringToUint8(request.FormValue("role_id"))
if err != nil {
http.Error(response, "bad roleid", http.StatusBadRequest)
http.Error(response, "bad role_id", http.StatusBadRequest)
return
}
if roleId > usedRoleId {
http.Error(response, "target role higher in hierarchy", http.StatusForbidden)
return
}
color, err := convertions.StringToRgba(request.FormValue("newcolor"))
color, err := convertions.StringToRgba(request.FormValue("new_color"))
if err != nil {
http.Error(response, "invalid newcolor", http.StatusBadRequest)
return
@@ -709,6 +774,7 @@ func HandleRoleSetColor(response http.ResponseWriter, request *http.Request) {
hub.Mu.Lock()
role := hub.Roles[roleId]
if role == nil {
hub.Mu.Unlock()
http.Error(response, "no such role", http.StatusNotFound)
return
}
@@ -722,9 +788,9 @@ func HandleRoleSetPermissions(response http.ResponseWriter, request *http.Reques
if !ok {
return
}
targetRoleId, err := convertions.StringToUint8(request.FormValue("roleid"))
targetRoleId, err := convertions.StringToUint8(request.FormValue("role_id"))
if err != nil {
http.Error(response, "bad roleid", http.StatusBadRequest)
http.Error(response, "bad role_id", http.StatusBadRequest)
return
}
if targetRoleId > usedRoleId {
@@ -814,13 +880,16 @@ func HandleChannelCreate(response http.ResponseWriter, request *http.Request) {
newHubChannel.RolesCanReadHistory.Add(types.HubBoundRolesMax)
}
hub.Mu.Lock()
for i, channel := range hub.Channels {
if channel != nil {
continue
}
hub.Channels[i] = newHubChannel
break
usedPositions := make(map[uint8]bool, len(hub.Channels))
for _, ch := range hub.Channels {
usedPositions[ch.Position] = true
}
var position uint8
for usedPositions[position] {
position++
}
newHubChannel.Position = position
hub.Channels[newHubChannel.Id] = newHubChannel
hub.Mu.Unlock()
updateChannelCacheForSpecChannel(newHubChannel, hub)
@@ -832,14 +901,19 @@ func HandleChannelRemove(response http.ResponseWriter, request *http.Request) {
if !ok {
return
}
channelId, err := convertions.StringToUint8(request.FormValue("id"))
channelId, err := convertions.StringToUuid(request.FormValue("channel_id"))
if err != nil {
http.Error(response, "invalid channel id", http.StatusBadRequest)
http.Error(response, "invalid channel_id", http.StatusBadRequest)
return
}
hub.Mu.Lock()
hub.Channels[channelId] = nil
if _, ok := hub.Channels[channelId]; !ok {
hub.Mu.Unlock()
http.Error(response, "no such channel", http.StatusNotFound)
return
}
delete(hub.Channels, channelId)
hub.Mu.Unlock()
response.WriteHeader(http.StatusAccepted)
}
@@ -848,26 +922,26 @@ func HandleChannelSetName(response http.ResponseWriter, request *http.Request) {
if !ok {
return
}
newName := request.FormValue("newname")
newName := request.FormValue("new_name")
if newName == "" {
http.Error(response, "newname empty", http.StatusBadRequest)
return
}
channelId, err := convertions.StringToUint8(request.FormValue("id"))
channelId, err := convertions.StringToUuid(request.FormValue("channel_id"))
if err != nil {
http.Error(response, "invalid channel id", http.StatusBadRequest)
http.Error(response, "invalid channel_id", http.StatusBadRequest)
return
}
hub.Mu.Lock()
channel := hub.Channels[channelId]
if channel == nil {
channel, ok := hub.Channels[channelId]
if !ok {
hub.Mu.Unlock()
http.Error(response, "no such channel", http.StatusNotFound)
return
}
channel.Name = newName
hub.Mu.Unlock()
response.WriteHeader(http.StatusAccepted)
}
@@ -877,20 +951,85 @@ func HandleChannelSetDescription(response http.ResponseWriter, request *http.Req
return
}
newDescription := request.FormValue("description")
channelId, err := convertions.StringToUint8(request.FormValue("id"))
channelId, err := convertions.StringToUuid(request.FormValue("channel_id"))
if err != nil {
http.Error(response, "invalid channel id", http.StatusBadRequest)
http.Error(response, "invalid channel_id", http.StatusBadRequest)
return
}
hub.Mu.Lock()
channel := hub.Channels[channelId]
if channel == nil {
channel, ok := hub.Channels[channelId]
if !ok {
hub.Mu.Unlock()
http.Error(response, "no such channel", http.StatusNotFound)
return
}
channel.Description = newDescription
hub.Mu.Unlock()
response.WriteHeader(http.StatusAccepted)
}
func handleChannelRolePermission(response http.ResponseWriter, request *http.Request, perm types.Permissions, modify func(*types.HubChannel, uint8, bool)) {
_, hub, _, usedRoleId, ok := hubPermissionContext(response, request, normal, perm)
if !ok {
return
}
channelId, err := convertions.StringToUuid(request.FormValue("channel_id"))
if err != nil {
http.Error(response, "invalid channel_id", http.StatusBadRequest)
return
}
roleId, err := convertions.StringToUint8(request.FormValue("role_id"))
if err != nil {
http.Error(response, "invalid role_id", http.StatusBadRequest)
return
}
if roleId > usedRoleId {
http.Error(response, "target role higher in hierarchy", http.StatusForbidden)
return
}
allow := convertions.StringToBool(request.URL.Query().Get("allow"))
hub.Mu.Lock()
channel, ok := hub.Channels[channelId]
if !ok {
hub.Mu.Unlock()
http.Error(response, "channel not found", http.StatusNotFound)
return
}
modify(channel, roleId, allow)
hub.Mu.Unlock()
updateChannelCacheForSpecChannel(channel, hub)
response.WriteHeader(http.StatusAccepted)
}
func HandleChannelSetPermittedVisibleRole(response http.ResponseWriter, request *http.Request) {
handleChannelRolePermission(response, request, types.PermissionSetChannelPermittedVisibleRoles, func(c *types.HubChannel, roleId uint8, allow bool) {
if allow {
c.RolesCanView.Add(roleId)
} else {
c.RolesCanView.Remove(roleId)
}
})
}
func HandleChannelSetPermittedSendRole(response http.ResponseWriter, request *http.Request) {
handleChannelRolePermission(response, request, types.PermissionSetChannelPermittedSendMessageRoles, func(c *types.HubChannel, roleId uint8, allow bool) {
if allow {
c.RolesCanMessage.Add(roleId)
} else {
c.RolesCanMessage.Remove(roleId)
}
})
}
func HandleChannelSetPermittedHistoryRole(response http.ResponseWriter, request *http.Request) {
handleChannelRolePermission(response, request, types.PermissionSetChannelPermittedReadHistoryRoles, func(c *types.HubChannel, roleId uint8, allow bool) {
if allow {
c.RolesCanReadHistory.Add(roleId)
} else {
c.RolesCanReadHistory.Remove(roleId)
}
})
}
+31 -3
View File
@@ -232,9 +232,9 @@ func HandleUserGetUser(response http.ResponseWriter, request *http.Request) {
return
}
targetId, err := convertions.StringToUuid(request.URL.Query().Get("targetid"))
targetId, err := convertions.StringToUuid(request.URL.Query().Get("target_id"))
if err != nil {
http.Error(response, "invalid userid", http.StatusBadRequest)
http.Error(response, "invalid target_id", http.StatusBadRequest)
return
}
target, err := getUserById(ctx, targetId)
@@ -265,7 +265,7 @@ func HandleUserGetUsers(response http.ResponseWriter, request *http.Request) {
return
}
targetIds, err := convertions.StringToUuids(request.URL.Query().Get("targetids"))
targetIds, err := convertions.StringToUuids(request.URL.Query().Get("target_ids"))
if err != nil {
http.Error(response, "invalid targetids", http.StatusBadRequest)
return
@@ -286,3 +286,31 @@ func HandleUserGetUsers(response http.ResponseWriter, request *http.Request) {
response.WriteHeader(http.StatusAccepted)
response.Write(userData)
}
func HandleGetHubs(response http.ResponseWriter, request *http.Request) {
if !validCheckWithResponseOnFail(response, request, normal) {
return
}
ctx := request.Context()
user, err := getUserByToken(ctx, request.Header.Get("token"))
if err != nil {
http.Error(response, "invalid token", http.StatusBadRequest)
return
}
user.Mu.RLock()
hubs := slices.Collect(maps.Keys(user.Hubs))
user.Mu.RUnlock()
if len(hubs) == 0 {
response.WriteHeader(http.StatusNoContent)
response.Write([]byte("no hubs found"))
return
}
converted, err := json.Marshal(hubs)
if err != nil {
http.Error(response, "json error", http.StatusInternalServerError)
return
}
response.WriteHeader(http.StatusOK)
response.Write(converted)
}