package grpc import ( "context" "github.com/sorti/openspeak/internal/channel" pb "github.com/sorti/openspeak/pkg/api/openspeak/v1" ) // ChannelServiceServer implements the ChannelService gRPC service type ChannelServiceServer struct { pb.UnimplementedChannelServiceServer server *Server } // NewChannelServiceServer creates a new ChannelServiceServer func NewChannelServiceServer(s *Server) *ChannelServiceServer { return &ChannelServiceServer{ server: s, } } // CreateChannel creates a new voice channel func (c *ChannelServiceServer) CreateChannel(ctx context.Context, req *pb.CreateChannelRequest) (*pb.CreateChannelResponse, error) { // Extract user ID from token userID := extractUserIDFromContext(ctx) if userID == "" { return nil, ErrUnauthorized } if req.Name == "" { return nil, ErrInvalidChannelName } ch, err := c.server.channelManager.CreateChannel(req.Name, userID) if err != nil { if err == channel.ErrChannelAlreadyExists { return nil, ErrChannelAlreadyExists } if err == channel.ErrInvalidChannelName { return nil, ErrInvalidChannelName } return nil, err } return &pb.CreateChannelResponse{ Status: &pb.Status{Success: true}, Channel: convertChannelToProto(ch), }, nil } // GetChannel retrieves a channel by ID func (c *ChannelServiceServer) GetChannel(ctx context.Context, req *pb.GetChannelRequest) (*pb.Channel, error) { if req.ChannelId == "" { return nil, ErrInvalidChannel } ch, err := c.server.channelManager.GetChannel(req.ChannelId) if err != nil { if err == channel.ErrChannelNotFound { return nil, ErrChannelNotFound } return nil, err } return convertChannelToProto(ch), nil } // ListChannels lists all active channels func (c *ChannelServiceServer) ListChannels(ctx context.Context, req *pb.ListChannelsRequest) (*pb.ListChannelsResponse, error) { channels := c.server.channelManager.ListChannels() pbChannels := make([]*pb.Channel, 0, len(channels)) for _, ch := range channels { pbChannels = append(pbChannels, convertChannelToProto(ch)) } return &pb.ListChannelsResponse{ Channels: pbChannels, }, nil } // UpdateChannel updates channel properties func (c *ChannelServiceServer) UpdateChannel(ctx context.Context, req *pb.UpdateChannelRequest) (*pb.Channel, error) { if req.ChannelId == "" { return nil, ErrInvalidChannel } ch, err := c.server.channelManager.UpdateChannel( req.ChannelId, req.Name, req.Description, req.IsPublic, req.MaxUsers, ) if err != nil { if err == channel.ErrChannelNotFound { return nil, ErrChannelNotFound } if err == channel.ErrChannelAlreadyExists { return nil, ErrChannelAlreadyExists } return nil, err } return convertChannelToProto(ch), nil } // DeleteChannel deletes a channel func (c *ChannelServiceServer) DeleteChannel(ctx context.Context, req *pb.DeleteChannelRequest) (*pb.Status, error) { if req.ChannelId == "" { return nil, ErrInvalidChannel } err := c.server.channelManager.DeleteChannel(req.ChannelId, req.HardDelete) if err != nil { if err == channel.ErrChannelNotFound { return nil, ErrChannelNotFound } return nil, err } return &pb.Status{ Success: true, }, nil } // JoinChannel adds the user to a channel func (c *ChannelServiceServer) JoinChannel(ctx context.Context, req *pb.JoinChannelRequest) (*pb.JoinChannelResponse, error) { userID := extractUserIDFromContext(ctx) if userID == "" { return nil, ErrUnauthorized } if req.ChannelId == "" { return nil, ErrInvalidChannel } ch, err := c.server.channelManager.JoinChannel(req.ChannelId, userID) if err != nil { if err == channel.ErrChannelNotFound { return nil, ErrChannelNotFound } if err == channel.ErrChannelFull { return nil, ErrChannelFull } return nil, err } // Create presence session c.server.presenceManager.CreateSession(userID) c.server.presenceManager.SetChannelPresence(userID, req.ChannelId) return &pb.JoinChannelResponse{ Status: &pb.Status{Success: true}, Channel: convertChannelToProto(ch), }, nil } // LeaveChannel removes the user from a channel func (c *ChannelServiceServer) LeaveChannel(ctx context.Context, req *pb.LeaveChannelRequest) (*pb.Status, error) { userID := extractUserIDFromContext(ctx) if userID == "" { return nil, ErrUnauthorized } if req.ChannelId == "" { return nil, ErrInvalidChannel } err := c.server.channelManager.LeaveChannel(req.ChannelId, userID) if err != nil { if err == channel.ErrChannelNotFound { return nil, ErrChannelNotFound } if err == channel.ErrUserNotInChannel { return nil, ErrUserNotInChannel } return nil, err } return &pb.Status{ Success: true, }, nil } // ListMembers lists members of a channel func (c *ChannelServiceServer) ListMembers(ctx context.Context, req *pb.ListMembersRequest) (*pb.ListMembersResponse, error) { if req.ChannelId == "" { return nil, ErrInvalidChannel } members, err := c.server.channelManager.GetChannelMembers(req.ChannelId) if err != nil { if err == channel.ErrChannelNotFound { return nil, ErrChannelNotFound } return nil, err } return &pb.ListMembersResponse{ MemberIds: members, }, nil } // SubscribeChannelEvents subscribes to channel events func (c *ChannelServiceServer) SubscribeChannelEvents(req *pb.SubscribeChannelEventsRequest, stream pb.ChannelService_SubscribeChannelEventsServer) error { // This is a streaming endpoint - would need to implement a proper event system // For now, return not implemented return ErrNotImplemented } // convertChannelToProto converts internal channel to proto format func convertChannelToProto(ch *channel.Channel) *pb.Channel { return &pb.Channel{ Id: ch.ID, Name: ch.Name, Description: ch.Description, IsPublic: ch.IsPublic, OwnerId: ch.OwnerID, MemberIds: ch.GetMembers(), MaxUsers: ch.MaxUsers, CreatedAt: ch.CreatedAt.Unix(), UpdatedAt: ch.UpdatedAt.Unix(), } } // extractUserIDFromContext extracts user ID from context func extractUserIDFromContext(ctx context.Context) string { // In a real implementation, extract from JWT claims // For MVP, use a default user ID if userID, ok := ctx.Value("userID").(string); ok { return userID } return "default-user" }