diff --git a/internal/op/room.go b/internal/op/room.go index 320a6f9..4e5f76b 100644 --- a/internal/op/room.go +++ b/internal/op/room.go @@ -584,7 +584,7 @@ func (r *Room) RemoveMemberPermissions(userID string, permissions model.RoomMemb func (r *Room) ApprovePendingMember(userID string) error { if r.IsCreator(userID) { - return errors.New("you are creator, cannot approve") + return errors.New("creator cannot be approved as a pending member") } defer r.members.Delete(userID) return db.RoomApprovePendingMember(r.ID, userID) @@ -592,7 +592,7 @@ func (r *Room) ApprovePendingMember(userID string) error { func (r *Room) BanMember(userID string) error { if r.IsCreator(userID) { - return errors.New("you are creator, cannot ban") + return errors.New("creator cannot be banned") } if r.IsGuest(userID) { return errors.New("please set whether to disable guest users in the room settings") @@ -606,7 +606,7 @@ func (r *Room) BanMember(userID string) error { func (r *Room) UnbanMember(userID string) error { if r.IsCreator(userID) { - return errors.New("you are creator, cannot unban") + return errors.New("creator cannot be unbanned") } if r.IsGuest(userID) { return errors.New("please set whether to enable guest users in the room settings") @@ -617,7 +617,7 @@ func (r *Room) UnbanMember(userID string) error { func (r *Room) DeleteMember(userID string) error { if r.IsCreator(userID) { - return errors.New("you are creator, cannot delete") + return errors.New("creator cannot be deleted") } defer func() { r.members.Delete(userID) @@ -632,7 +632,7 @@ func (r *Room) ResetAdminPermissions(userID string) error { func (r *Room) SetAdminPermissions(userID string, permissions model.RoomAdminPermission) error { if r.IsCreator(userID) { - return errors.New("you are creator, cannot set admin permissions") + return errors.New("creator cannot set admin permissions") } if r.IsGuest(userID) { return errors.New("cannot set admin permissions to guest") @@ -648,7 +648,7 @@ func (r *Room) SetAdminPermissions(userID string, permissions model.RoomAdminPer func (r *Room) AddAdminPermissions(userID string, permissions model.RoomAdminPermission) error { if r.IsCreator(userID) { - return errors.New("you are creator, cannot add admin permissions") + return errors.New("creator cannot add admin permissions") } if r.IsGuest(userID) { return errors.New("cannot add admin permissions to guest") @@ -664,7 +664,7 @@ func (r *Room) AddAdminPermissions(userID string, permissions model.RoomAdminPer func (r *Room) RemoveAdminPermissions(userID string, permissions model.RoomAdminPermission) error { if r.IsCreator(userID) { - return errors.New("you are creator, cannot remove admin permissions") + return errors.New("creator cannot remove admin permissions") } if r.IsGuest(userID) { return errors.New("cannot remove admin permissions from guest") @@ -680,7 +680,7 @@ func (r *Room) RemoveAdminPermissions(userID string, permissions model.RoomAdmin func (r *Room) SetAdmin(userID string, permissions model.RoomAdminPermission) error { if r.IsCreator(userID) { - return errors.New("you are creator, cannot set admin") + return errors.New("creator cannot set admin") } if r.IsGuest(userID) { return errors.New("cannot set guest as admin") @@ -691,7 +691,7 @@ func (r *Room) SetAdmin(userID string, permissions model.RoomAdminPermission) er func (r *Room) SetMember(userID string, permissions model.RoomMemberPermission) error { if r.IsCreator(userID) { - return errors.New("you are creator, cannot set member") + return errors.New("creator cannot set member") } defer r.members.Delete(userID) return db.RoomSetMember(r.ID, userID, permissions) diff --git a/internal/op/rooms.go b/internal/op/rooms.go index f94aa0f..d614622 100644 --- a/internal/op/rooms.go +++ b/internal/op/rooms.go @@ -13,12 +13,10 @@ import ( var ( roomCache *synccache.SyncCache[string, *Room] - ErrRoomPending = errors.New("room pending, please wait for admin to approve") - ErrRoomBanned = errors.New("room banned") - ErrRoomCreatorBanned = errors.New("room creator banned") - ErrRoomCreatorPending = errors.New("room creator pending, please wait for admin to approve") - ErrInvalidRoomID = errors.New("room id is not 32 bit") - ErrRoomNotInCache = errors.New("room is not in cache") + ErrRoomCreatorBanned = errors.New("room creator is banned") + ErrRoomCreatorPending = errors.New("room creator is pending approval, please wait for admin to review") + ErrInvalidRoomID = errors.New("invalid room ID: must be 32 characters long") + ErrRoomNotInCache = errors.New("room not found in cache") ) type RoomEntry = synccache.Entry[*Room] diff --git a/internal/op/users.go b/internal/op/users.go index 995d208..c9706db 100644 --- a/internal/op/users.go +++ b/internal/op/users.go @@ -16,8 +16,8 @@ var userCache *synccache.SyncCache[string, *User] type UserEntry = synccache.Entry[*User] var ( - ErrUserBanned = errors.New("user banned") - ErrUserPending = errors.New("user pending, please wait for admin to approve") + ErrUserBanned = errors.New("user account has been banned") + ErrUserPending = errors.New("user account is pending approval, please wait for administrator review") ) func LoadOrInitUser(u *model.User) (*UserEntry, error) { diff --git a/server/handlers/room.go b/server/handlers/room.go index 8c4e72e..13fabbf 100644 --- a/server/handlers/room.go +++ b/server/handlers/room.go @@ -69,6 +69,7 @@ func RoomInfo(ctx *gin.Context) { "name": room.Name, "needPassword": room.NeedPassword(), "creator": op.GetUserName(room.CreatorID), + "creatorId": room.CreatorID, "createdAt": room.CreatedAt.UnixMilli(), "status": room.Status, "enabledGuest": room.EnabledGuest(), @@ -128,9 +129,10 @@ var roomHotCache = refreshcache0.NewRefreshCache[[]*model.RoomListResp](func(con rooms = append(rooms, &model.RoomListResp{ RoomId: v.ID, RoomName: v.Name, - PeopleNum: v.ViewerCount(), + ViewerCount: v.ViewerCount(), NeedPassword: v.NeedPassword(), Creator: op.GetUserName(v.CreatorID), + CreatorID: v.CreatorID, CreatedAt: v.CreatedAt.UnixMilli(), }) } @@ -138,7 +140,7 @@ var roomHotCache = refreshcache0.NewRefreshCache[[]*model.RoomListResp](func(con }) slices.SortStableFunc(rooms, func(a, b *model.RoomListResp) int { - if a.PeopleNum == b.PeopleNum { + if a.ViewerCount == b.ViewerCount { if a.RoomName == b.RoomName { return 0 } @@ -147,7 +149,7 @@ var roomHotCache = refreshcache0.NewRefreshCache[[]*model.RoomListResp](func(con } else { return 1 } - } else if a.PeopleNum > b.PeopleNum { + } else if a.ViewerCount > b.ViewerCount { return -1 } else { return 1 @@ -274,7 +276,7 @@ func genRoomListResp(scopes ...func(db *gorm.DB) *gorm.DB) ([]*model.RoomListRes resp[i] = &model.RoomListResp{ RoomId: r.ID, RoomName: r.Name, - PeopleNum: op.ViewerCount(r.ID), + ViewerCount: op.ViewerCount(r.ID), NeedPassword: len(r.HashedPassword) != 0, CreatorID: r.CreatorID, Creator: op.GetUserName(r.CreatorID), @@ -299,7 +301,7 @@ func genJoinedRoomListResp(scopes ...func(db *gorm.DB) *gorm.DB) ([]*model.Joine RoomListResp: model.RoomListResp{ RoomId: r.ID, RoomName: r.Name, - PeopleNum: op.ViewerCount(r.ID), + ViewerCount: op.ViewerCount(r.ID), NeedPassword: len(r.HashedPassword) != 0, CreatorID: r.CreatorID, Creator: op.GetUserName(r.CreatorID), @@ -330,14 +332,14 @@ func CheckRoom(ctx *gin.Context) { } room := roomE.Value() - ctx.JSON(http.StatusOK, model.NewApiDataResp(gin.H{ - "name": room.Name, - "status": room.Status, - "peopleNum": op.ViewerCount(room.ID), - "needPassword": room.NeedPassword(), - "creatorId": room.CreatorID, - "creator": op.GetUserName(room.CreatorID), - "enabledGuest": room.EnabledGuest(), + ctx.JSON(http.StatusOK, model.NewApiDataResp(&model.CheckRoomResp{ + Name: room.Name, + Status: room.Status, + CreatorID: room.CreatorID, + Creator: op.GetUserName(room.CreatorID), + NeedPassword: room.NeedPassword(), + ViewerCount: op.ViewerCount(room.ID), + EnabledGuest: room.EnabledGuest(), })) } @@ -355,15 +357,23 @@ func LoginRoom(ctx *gin.Context) { roomE, err := op.LoadOrInitRoomByID(req.RoomId) if err != nil { log.Errorf("login room failed: %v", err) - if err == op.ErrRoomBanned || err == op.ErrRoomPending { - ctx.AbortWithStatusJSON(http.StatusForbidden, model.NewApiErrorResp(err)) - return - } - ctx.AbortWithStatusJSON(http.StatusNotFound, model.NewApiErrorResp(err)) + ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) return } room := roomE.Value() + if room.IsBanned() { + log.Warn("login room failed: room is banned") + ctx.AbortWithStatusJSON(http.StatusForbidden, model.NewApiErrorStringResp("room is banned")) + return + } + + if room.IsPending() { + log.Warn("login room failed: room is pending, please wait for admin to approve") + ctx.AbortWithStatusJSON(http.StatusForbidden, model.NewApiErrorStringResp("room is pending, please wait for admin to approve")) + return + } + if member, err := room.LoadMember(user.ID); err == nil { ctx.JSON(http.StatusOK, model.NewApiDataResp(gin.H{ "status": member.Status, diff --git a/server/handlers/user.go b/server/handlers/user.go index bb0a5fa..5298086 100644 --- a/server/handlers/user.go +++ b/server/handlers/user.go @@ -305,6 +305,15 @@ func UserCheckJoinedRoom(ctx *gin.Context) { ctx.JSON(http.StatusOK, model.NewApiDataResp(gin.H{ "joined": status != dbModel.RoomMemberStatusNotJoined, "status": status, + "room": &model.CheckRoomResp{ + Name: room.Name, + Status: room.Status, + CreatorID: room.CreatorID, + Creator: op.GetUserName(room.CreatorID), + NeedPassword: room.NeedPassword(), + ViewerCount: op.ViewerCount(room.ID), + EnabledGuest: room.EnabledGuest(), + }, })) } diff --git a/server/middlewares/auth.go b/server/middlewares/auth.go index 008d078..f4c3a93 100644 --- a/server/middlewares/auth.go +++ b/server/middlewares/auth.go @@ -2,7 +2,6 @@ package middlewares import ( "errors" - "fmt" "net/http" "strings" "time" @@ -20,8 +19,20 @@ import ( ) var ( - ErrAuthFailed = errors.New("auth failed") - ErrAuthExpired = errors.New("auth expired") + ErrAuthFailed = errors.New("authentication failed") + ErrAuthExpired = errors.New("authentication token expired") + ErrUserBanned = errors.New("user account has been banned") + ErrUserPending = errors.New("user account is pending approval") + ErrUserGuest = errors.New("guests are not allowed to perform this action") + ErrRoomBanned = errors.New("room has been banned") + ErrRoomPending = errors.New("room is pending approval") + ErrUserBannedFromRoom = errors.New("user has been banned from this room") + ErrInvalidRoomID = errors.New("invalid room ID") + ErrEmptyToken = errors.New("authentication token is empty") + ErrNotRoomAdmin = errors.New("user is not a room administrator") + ErrNotRoomCreator = errors.New("user is not the room creator") + ErrNotAdmin = errors.New("user is not an administrator") + ErrNotRoot = errors.New("user is not a root user") ) type AuthClaims struct { @@ -46,7 +57,7 @@ func authUser(authorization string) (*AuthClaims, error) { func AuthRoom(authorization, roomId string) (*op.UserEntry, *op.RoomEntry, error) { if len(roomId) != 32 { - return nil, nil, ErrAuthFailed + return nil, nil, ErrInvalidRoomID } userE, err := authenticateUserOrGuest(authorization) @@ -95,23 +106,23 @@ func authenticateUser(Authorization string) (*op.UserEntry, error) { func authenticateGuest() (*op.UserEntry, error) { if !settings.EnableGuest.Get() { - return nil, fmt.Errorf("guests are disabled") + return nil, ErrUserGuest } return op.LoadOrInitGuestUser() } func validateUser(user *op.User, userVersion uint32) error { if user.IsGuest() { - return fmt.Errorf("guests are not allowed to join rooms by token") + return ErrUserGuest } if !user.CheckVersion(userVersion) { return ErrAuthExpired } if user.IsBanned() { - return fmt.Errorf("user is banned") + return ErrUserBanned } if user.IsPending() { - return fmt.Errorf("user is pending, need admin to approve") + return ErrUserPending } return nil } @@ -133,18 +144,18 @@ func authenticateRoomAccess(roomId string, user *op.User) (*op.RoomEntry, error) func validateRoomAccess(room *op.Room, user *op.User) error { if room.IsGuest(user.ID) { if room.Settings.DisableGuest { - return fmt.Errorf("guests are not allowed to join rooms") + return ErrUserGuest } if room.NeedPassword() { - return fmt.Errorf("guests are not allowed to join rooms that require a password") + return ErrUserGuest } } if room.IsBanned() { - return fmt.Errorf("room is banned") + return ErrRoomBanned } if room.IsPending() { - return fmt.Errorf("room is pending, need admin to approve") + return ErrRoomPending } var status dbModel.RoomMemberStatus @@ -159,10 +170,10 @@ func validateRoomAccess(room *op.Room, user *op.User) error { } if status.IsBanned() { - return fmt.Errorf("user is banned from room") + return ErrUserBannedFromRoom } if status.IsPending() { - return fmt.Errorf("user is pending, need admin to approve") + return ErrUserPending } return nil @@ -193,16 +204,16 @@ func AuthUser(authorization string) (*op.UserEntry, error) { func validateAuthUser(user *op.User, userVersion uint32) error { if user.IsGuest() { - return errors.New("user is guest, cannot login") + return ErrUserGuest } if !user.CheckVersion(userVersion) { return ErrAuthExpired } if user.IsBanned() { - return errors.New("user is banned") + return ErrUserBanned } if user.IsPending() { - return errors.New("user is pending, need admin to approve") + return ErrUserPending } return nil } @@ -228,12 +239,6 @@ func NewAuthUserToken(user *op.User) (string, error) { return jwt.NewWithClaims(jwt.SigningMethodHS256, claims).SignedString(stream.StringToBytes(conf.Conf.Jwt.Secret)) } -var ( - ErrUserBanned = errors.New("user banned") - ErrUserPending = errors.New("user is pending, need admin to approve") - ErrUserGuest = errors.New("user is guest, cannot login") -) - func validateNewAuthUserToken(user *op.User) error { if user.IsBanned() { return ErrUserBanned @@ -250,7 +255,7 @@ func validateNewAuthUserToken(user *op.User) error { func AuthUserMiddleware(ctx *gin.Context) { token := GetAuthorizationTokenFromContext(ctx) if token == "" { - ctx.AbortWithStatusJSON(http.StatusUnauthorized, model.NewApiErrorStringResp("token is empty")) + ctx.AbortWithStatusJSON(http.StatusUnauthorized, model.NewApiErrorResp(ErrEmptyToken)) return } userE, err := AuthUser(token) @@ -291,7 +296,7 @@ func AuthRoomWithoutGuestMiddleware(ctx *gin.Context) { user := ctx.MustGet("user").(*synccache.Entry[*op.User]).Value() if user.IsGuest() { - ctx.AbortWithStatusJSON(http.StatusForbidden, model.NewApiErrorStringResp("guest has no permission")) + ctx.AbortWithStatusJSON(http.StatusForbidden, model.NewApiErrorResp(ErrUserGuest)) return } } @@ -306,7 +311,7 @@ func AuthRoomAdminMiddleware(ctx *gin.Context) { user := ctx.MustGet("user").(*synccache.Entry[*op.User]).Value() if !user.IsRoomAdmin(room) { - ctx.AbortWithStatusJSON(http.StatusForbidden, model.NewApiErrorStringResp("user has no permission")) + ctx.AbortWithStatusJSON(http.StatusForbidden, model.NewApiErrorResp(ErrNotRoomAdmin)) return } } @@ -321,7 +326,7 @@ func AuthRoomCreatorMiddleware(ctx *gin.Context) { user := ctx.MustGet("user").(*synccache.Entry[*op.User]).Value() if room.CreatorID != user.ID { - ctx.AbortWithStatusJSON(http.StatusForbidden, model.NewApiErrorStringResp("user is not creator")) + ctx.AbortWithStatusJSON(http.StatusForbidden, model.NewApiErrorResp(ErrNotRoomCreator)) return } } @@ -334,7 +339,7 @@ func AuthAdminMiddleware(ctx *gin.Context) { userE := ctx.MustGet("user").(*synccache.Entry[*op.User]) if !userE.Value().IsAdmin() { - ctx.AbortWithStatusJSON(http.StatusForbidden, model.NewApiErrorStringResp("user is not admin")) + ctx.AbortWithStatusJSON(http.StatusForbidden, model.NewApiErrorResp(ErrNotAdmin)) return } } @@ -347,7 +352,7 @@ func AuthRootMiddleware(ctx *gin.Context) { userE := ctx.MustGet("user").(*synccache.Entry[*op.User]) if !userE.Value().IsRoot() { - ctx.AbortWithStatusJSON(http.StatusForbidden, model.NewApiErrorStringResp("user is not root")) + ctx.AbortWithStatusJSON(http.StatusForbidden, model.NewApiErrorResp(ErrNotRoot)) return } } @@ -392,11 +397,11 @@ func GetRoomIdFromContext(ctx *gin.Context) (string, error) { return roomId, nil } ctx.Set("roomId", "") - return "", errors.New("room id length is not 32") + return "", ErrInvalidRoomID } ctx.Set("roomId", "") - return "", errors.New("room id is empty") + return "", ErrInvalidRoomID } func setLogFields(ctx *gin.Context, user *op.User, room *op.Room) { diff --git a/server/model/room.go b/server/model/room.go index c61fbb4..6c918c7 100644 --- a/server/model/room.go +++ b/server/model/room.go @@ -61,7 +61,7 @@ func (c *CreateRoomReq) Validate() error { type RoomListResp struct { RoomId string `json:"roomId"` RoomName string `json:"roomName"` - PeopleNum int64 `json:"peopleNum"` + ViewerCount int64 `json:"viewerCount"` NeedPassword bool `json:"needPassword"` CreatorID string `json:"creatorId"` Creator string `json:"creator"` @@ -150,3 +150,13 @@ func (c *CheckRoomPasswordReq) Decode(ctx *gin.Context) error { func (c *CheckRoomPasswordReq) Validate() error { return nil } + +type CheckRoomResp struct { + Name string `json:"name"` + Status model.RoomStatus `json:"status"` + CreatorID string `json:"creatorId"` + Creator string `json:"creator"` + NeedPassword bool `json:"needPassword"` + ViewerCount int64 `json:"viewerCount"` + EnabledGuest bool `json:"enabledGuest"` +}