Files
go-socket/packages/httpRequest/hubs.go
T
2026-04-30 11:44:54 +02:00

323 lines
8.4 KiB
Go

package httpRequest
import (
"context"
"encoding/json"
"go-socket/packages/convertions"
"maps"
"net/http"
"slices"
"strings"
"time"
"go-socket/packages/cache"
"go-socket/packages/types"
"go-socket/packages/wsServer"
"github.com/google/uuid"
)
func haveHubUserPermission(u *types.HubUser, h *types.Hub, needed types.Permissions) bool {
h.Mu.Lock()
defer h.Mu.Unlock()
for _, role := range h.Roles {
if role == nil {
continue
}
if !u.Roles.Has(role.Id) {
continue
}
if (needed & role.Permissions) == needed {
return true
}
}
return false
}
func haveHubUserCachedPermissions(needed types.CachedUserPermissions, user *types.HubUser, channel *types.HubChannel) bool {
channel.Mu.RLock()
checkAgainst, ok := channel.UsersCachedPermissions[user.OriginalId]
channel.Mu.RUnlock()
if !ok || (needed&checkAgainst) != needed {
return false
}
return true
}
func HandleHubCreate(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.StatusUnauthorized)
return
}
hubName := request.FormValue("hubname")
if hubName == "" {
http.Error(response, "hub name is required", http.StatusBadRequest)
return
}
hub := types.NewHub()
hub.Name = hubName
hub.Color = types.RandomRgba()
hub.Id = uuid.New()
hub.Creator = user.Id
hub.CreatedAt = time.Now()
user.Hubs[user.Id] = hub
creator := types.NewHubUser()
creator.OriginalId = user.Id
creator.CreatedAt = hub.CreatedAt
hub.Users[creator.OriginalId] = creator
rootRole := &types.HubRole{
Id: uint8(0),
Permissions: types.PermissionAll(),
Name: "root",
Color: types.RandomRgba(),
CreatedAt: hub.CreatedAt,
}
hub.Roles[rootRole.Id] = rootRole
creator.Roles.Add(rootRole.Id)
memberRole := &types.HubRole{
Id: types.HubBoundRolesMax,
Name: "member",
Color: types.RandomRgba(),
CreatedAt: hub.CreatedAt,
}
hub.JoinRole = memberRole
hub.Roles[memberRole.Id] = memberRole
creator.Roles.Add(memberRole.Id)
channel := types.NewHubChannel()
channel.Name = "main channel"
channel.Position = uint8(0)
channel.Id = uuid.New()
channel.Description = "The fist channel!"
channel.CreatedAt = hub.CreatedAt
channel.RolesCanMessage.Add(rootRole.Id)
channel.RolesCanMessage.Add(memberRole.Id)
channel.RolesCanView.Add(rootRole.Id)
channel.RolesCanView.Add(memberRole.Id)
channel.RolesCanReadHistory.Add(rootRole.Id)
channel.RolesCanReadHistory.Add(memberRole.Id)
channel.UsersCachedPermissions[creator.OriginalId] = types.CachedUserPermissionsAll
hub.Channels[channel.Id] = channel
cache.SaveHub(hub)
}
func HandleHubJoin(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.StatusUnauthorized)
return
}
hub, err := getHubByIdStr(ctx, request.Header.Get("hubid"))
if err != nil {
http.Error(response, "invalid hubid", http.StatusBadRequest)
return
}
hubUser := types.NewHubUser()
hubUser.OriginalId = user.Id
hubUser.Roles.Add(hub.JoinRole.Id)
hubUser.CreatedAt = time.Now()
hub.Users[hubUser.OriginalId] = hubUser
}
func HandleHubMessage(response http.ResponseWriter, request *http.Request) {
if !validCheckWithResponseOnFail(response, request, normal) {
return
}
ctx := request.Context()
user, hubUser, hub, err := getHubUserIfValidWithResponseOnFail(ctx, response, request)
if err != nil {
return
}
channel, err := getHubChannelIfValidWithResponseOnFail(ctx, response, hub, hubUser, request.Header.Get("channelid"))
if err != nil {
return
}
if hubUser.IsMuted {
http.Error(response, "muted", http.StatusForbidden)
return
}
msgContent := request.FormValue("msgContent")
attachedFile := request.FormValue("attachedFile")
if msgContent == "" && attachedFile == "" {
http.Error(response, "empty msgContent", http.StatusBadRequest)
return
}
if attachedFile != "" && !strings.HasPrefix(attachedFile, channel.Id.String()+"/") {
http.Error(response, "invalid attachedFile", http.StatusBadRequest)
return
}
channel.Mu.RLock()
perms := channel.UsersCachedPermissions
channel.Mu.RUnlock()
msg := &types.Message{
Id: uuid.New(),
AttachedFile: attachedFile,
Content: msgContent,
Sender: user.Id,
Receiver: channel.Id,
CreatedAt: time.Now(),
}
for permId, perm := range perms {
if !perm.CanReadHistory() {
continue
}
target, err := cache.GetUserById(permId)
if err != nil {
continue
}
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)
}
func hubPermissionContext(response http.ResponseWriter, request *http.Request, rt bodyLimit, needed types.Permissions) (*types.HubUser, *types.Hub, context.Context, bool) {
if !validCheckWithResponseOnFail(response, request, rt) {
return nil, nil, nil, false
}
ctx := request.Context()
_, hubUser, hub, err := getHubUserIfValidWithResponseOnFail(ctx, response, request)
if err != nil {
return nil, nil, nil, false
}
if !haveHubUserPermission(hubUser, hub, needed) {
http.Error(response, "", http.StatusForbidden)
return nil, nil, nil, false
}
return hubUser, hub, ctx, true
}
func HandleHubSetName(response http.ResponseWriter, request *http.Request) {
_, hub, _, ok := hubPermissionContext(response, request, normal, types.PermissionSetHubName)
if !ok {
return
}
newName := request.FormValue("newname")
if newName == "" {
http.Error(response, "empty name", http.StatusBadRequest)
return
}
hub.Name = newName
response.WriteHeader(http.StatusAccepted)
}
func HandleHubSetColor(response http.ResponseWriter, request *http.Request) {
_, hub, _, ok := hubPermissionContext(response, request, normal, types.PermissionSetHubColor)
if !ok {
return
}
color, err := convertions.StringToRgba(request.FormValue("newname"))
if err != nil {
http.Error(response, "bad color", http.StatusBadRequest)
return
}
hub.Color = color
response.WriteHeader(http.StatusAccepted)
}
func HandleHubRemove(response http.ResponseWriter, request *http.Request) {
_, hub, _, ok := hubPermissionContext(response, request, normal, types.PermissionRemoveHub)
if !ok {
return
}
cache.DeleteHub(hub)
response.WriteHeader(http.StatusAccepted)
}
func HandleHubToggleUserColorAllowed(response http.ResponseWriter, request *http.Request) {
_, hub, _, ok := hubPermissionContext(response, request, normal, types.PermissionSetUserColorAllowed)
if !ok {
return
}
hub.UserColorAllowed = !hub.UserColorAllowed
response.WriteHeader(http.StatusAccepted)
}
func HandleHubUserRemove(response http.ResponseWriter, request *http.Request) {
_, hub, _, ok := hubPermissionContext(response, request, normal, types.PermissionRemoveUser)
if !ok {
return
}
color, err := convertions.StringToRgba(request.FormValue("newname"))
if err != nil {
http.Error(response, "bad color", http.StatusBadRequest)
return
}
hub.Color = color
response.WriteHeader(http.StatusAccepted)
}
func HandleHubRenameUser(response http.ResponseWriter, request *http.Request) {
_, hub, _, ok := hubPermissionContext(response, request, normal, types.PermissionRenameUser)
if !ok {
return
}
newName := request.FormValue("newname")
targetId, err := convertions.StringToUuid(request.FormValue("targetid"))
if err != nil {
http.Error(response, "bad targetid", http.StatusBadRequest)
return
}
hub.Mu.RLock()
target, ok := hub.Users[targetId]
if !ok {
http.Error(response, "no users found", http.StatusNotFound)
return
}
hub.Mu.RUnlock()
target.Name = newName
response.WriteHeader(http.StatusAccepted)
}