package room import ( "context" "database/sql" "errors" ) type Service struct { store *Store } func NewService(store *Store) *Service { return &Service{store: store} } func (s *Service) Store() *Store { return s.store } func (s *Service) GetRoom(ctx context.Context, name string) (*Room, error) { if s.store == nil || s.store.db == nil { return nil, errors.New("room store not configured") } return s.store.GetRoom(ctx, name) } func (s *Service) CreateRoom(ctx context.Context, name, creator string, settings *RoomSettings) (*Room, error) { if name == "" { return nil, errors.New("room name required") } if creator == "" { return nil, errors.New("creator required") } r := NewRoom(name, creator) if settings != nil { r.Settings = settings r.IsPublic = settings.IsPublic r.RequireApproval = settings.RequireApproval if settings.MaxMembers > 0 { r.MaxMembers = settings.MaxMembers } } if err := s.store.CreateRoom(ctx, r); err != nil { return nil, err } creatorMember := NewMember(name, creator, RoleCreator) if err := s.store.UpsertMember(ctx, creatorMember); err != nil { return nil, err } return r, nil } func (s *Service) JoinRoom(ctx context.Context, roomName, username string, password string) (*RoomMember, error) { r, err := s.store.GetRoom(ctx, roomName) if err != nil { return nil, err } if r.Status == RoomBanned { return nil, errors.New("room is banned") } if r.Status == RoomClosed { return nil, errors.New("room is closed") } if r.PasswordHash != "" { if !verifyPassword(password, r.PasswordHash) { return nil, errors.New("invalid room password") } } if cnt, _ := s.store.CountMembers(ctx, roomName); cnt >= r.MaxMembers && r.MaxMembers > 0 { return nil, errors.New("room is full") } member, err := s.store.GetMember(ctx, roomName, username) if err == nil && member.Status == MemberActive { return member, nil } if err == nil && member.Status == MemberBanned { return nil, errors.New("you are banned from this room") } newMember := NewMember(roomName, username, RoleMember) if s.store != nil { _ = s.store.UpsertMember(ctx, newMember) } return newMember, nil } func (s *Service) GetMember(ctx context.Context, room, userID string) (*RoomMember, error) { return s.store.GetMember(ctx, room, userID) } func (s *Service) EnsurePermission(ctx context.Context, room, userID string, perm RoomPermission) error { if s.store == nil || s.store.db == nil { return nil } r, err := s.store.GetRoom(ctx, room) if err != nil { if errors.Is(err, sql.ErrNoRows) { return errors.New("room not found") } return err } if r.Status == RoomBanned { return errors.New("room is banned") } if r.Status == RoomClosed && perm != PermViewMembers && perm != PermViewChatHistory { return errors.New("room is closed") } m, err := s.store.GetMember(ctx, room, userID) if err != nil { if errors.Is(err, sql.ErrNoRows) { if r.IsPublic || r.Settings.AllowGuestJoin { guest := NewMember(room, userID, RoleGuest) if guest.HasPermission(perm, r.Settings) { return nil } } return errors.New("not a member of this room") } return err } if m.Status != MemberActive { return errors.New("member not active") } if !m.HasPermission(perm, r.Settings) { return errors.New("permission denied: " + perm.String()) } return nil } func (s *Service) CheckPermission(ctx context.Context, room, userID string, perm RoomPermission) bool { return s.EnsurePermission(ctx, room, userID, perm) == nil } func (s *Service) ListMembers(ctx context.Context, room string) ([]*RoomMember, error) { return s.store.ListMembers(ctx, room) } func (s *Service) UpdateMemberRole(ctx context.Context, room, actorID, targetID string, newRole RoomRole) error { if newRole == RoleCreator { return errors.New("cannot assign creator role") } roomObj, err := s.store.GetRoom(ctx, room) if err != nil { return err } actor, err := s.store.GetMember(ctx, room, actorID) if err != nil { return errors.New("actor not in room") } if !actor.HasPermission(PermManageMembers, roomObj.Settings) { return errors.New("permission denied: manage members") } if !actor.Role.CanManage(newRole) { return errors.New("cannot manage role higher than yours") } target, err := s.store.GetMember(ctx, room, targetID) if err != nil { return errors.New("target not in room") } if target.Role == RoleCreator { return errors.New("cannot change creator role") } if !actor.Role.CanManage(target.Role) { return errors.New("cannot manage member with higher or equal role") } target.Role = newRole return s.store.UpsertMember(ctx, target) } func (s *Service) UpdateMemberPermissions(ctx context.Context, room, actorID, targetID string, added, removed uint64, isAdmin bool) error { roomObj, err := s.store.GetRoom(ctx, room) if err != nil { return err } actor, err := s.store.GetMember(ctx, room, actorID) if err != nil { return errors.New("actor not in room") } if !actor.HasPermission(PermManagePermissions, roomObj.Settings) { return errors.New("permission denied: manage permissions") } target, err := s.store.GetMember(ctx, room, targetID) if err != nil { return errors.New("target not in room") } if !actor.Role.CanManage(target.Role) { return errors.New("cannot manage this member") } if isAdmin { if target.Role != RoleAdmin && target.Role != RoleCreator { return errors.New("admin permissions only for admin/creator") } target.AdminAdded = added target.AdminRemoved = removed } else { target.AddedPerms = added target.RemovedPerms = removed } return s.store.UpsertMember(ctx, target) } func (s *Service) KickMember(ctx context.Context, room, actorID, targetID string) error { roomObj, err := s.store.GetRoom(ctx, room) if err != nil { return err } actor, err := s.store.GetMember(ctx, room, actorID) if err != nil { return errors.New("actor not in room") } if !actor.HasPermission(PermRemoveMembers, roomObj.Settings) { return errors.New("permission denied: remove members") } target, err := s.store.GetMember(ctx, room, targetID) if err != nil { return errors.New("target not in room") } if target.Role == RoleCreator { return errors.New("cannot kick creator") } if !actor.Role.CanManage(target.Role) { return errors.New("cannot kick higher role") } return s.store.RemoveMember(ctx, room, targetID) } func (s *Service) BanMember(ctx context.Context, room, actorID, targetID string) error { if err := s.KickMember(ctx, room, actorID, targetID); err != nil { return err } banned := NewMember(room, targetID, RoleGuest) banned.Status = MemberBanned return s.store.UpsertMember(ctx, banned) } func (s *Service) LeaveRoom(ctx context.Context, room, userID string) error { m, err := s.store.GetMember(ctx, room, userID) if err != nil { return err } if m.Role == RoleCreator { return errors.New("creator cannot leave, transfer ownership or delete room") } return s.store.RemoveMember(ctx, room, userID) } func (s *Service) UpdateRoomSettings(ctx context.Context, room, actorID string, newSettings *RoomSettings) error { r, err := s.store.GetRoom(ctx, room) if err != nil { return err } actor, err := s.store.GetMember(ctx, room, actorID) if err != nil { return errors.New("actor not in room") } if !actor.HasPermission(PermManageRoom, r.Settings) { return errors.New("permission denied: manage room") } return s.store.UpdateRoomSettings(ctx, room, newSettings) } func verifyPassword(input, hash string) bool { if hash == "" { return true } return input == hash }