package handler import ( "context" "tailly_clips/internal/domain" "tailly_clips/internal/service" "tailly_clips/proto" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "google.golang.org/protobuf/types/known/emptypb" "google.golang.org/protobuf/types/known/timestamppb" ) type GRPCHandler struct { proto.UnimplementedClipServiceServer clipService service.ClipService likeService service.LikeService commentService service.CommentService } func NewGRPCHandler( clipService service.ClipService, likeService service.LikeService, commentService service.CommentService, ) *GRPCHandler { return &GRPCHandler{ clipService: clipService, likeService: likeService, commentService: commentService, } } func (h *GRPCHandler) CreateClip(ctx context.Context, req *proto.CreateClipRequest) (*proto.CreateClipResponse, error) { createReq := domain.CreateClipRequest{ UserID: int(req.UserId), Title: req.Title, VideoData: req.VideoData, FileName: req.FileName, ContentType: req.ContentType, } clip, err := h.clipService.CreateClip(ctx, createReq) if err != nil { return nil, status.Errorf(codes.Internal, "failed to create clip: %v", err) } return &proto.CreateClipResponse{ Clip: h.clipToProto(clip), }, nil } func (h *GRPCHandler) GetClip(ctx context.Context, req *proto.GetClipRequest) (*proto.GetClipResponse, error) { clip, err := h.clipService.GetClip(ctx, int(req.ClipId)) if err != nil { return nil, status.Errorf(codes.NotFound, "clip not found: %v", err) } return &proto.GetClipResponse{ Clip: h.clipToProto(clip), }, nil } func (h *GRPCHandler) GetUserClips(ctx context.Context, req *proto.GetUserClipsRequest) (*proto.GetUserClipsResponse, error) { clips, totalCount, err := h.clipService.GetUserClips(ctx, int(req.UserId), int(req.Limit), int(req.Offset)) if err != nil { return nil, status.Errorf(codes.Internal, "failed to get user clips: %v", err) } return &proto.GetUserClipsResponse{ Clips: h.clipsToProto(clips), TotalCount: int32(totalCount), }, nil } func (h *GRPCHandler) GetClips(ctx context.Context, req *proto.GetClipsRequest) (*proto.GetClipsResponse, error) { clips, totalCount, err := h.clipService.GetClips(ctx, int(req.Limit), int(req.Offset)) if err != nil { return nil, status.Errorf(codes.Internal, "failed to get clips: %v", err) } return &proto.GetClipsResponse{ Clips: h.clipsToProto(clips), TotalCount: int32(totalCount), }, nil } func (h *GRPCHandler) DeleteClip(ctx context.Context, req *proto.DeleteClipRequest) (*emptypb.Empty, error) { err := h.clipService.DeleteClip(ctx, int(req.ClipId), int(req.UserId)) if err != nil { return nil, status.Errorf(codes.Internal, "failed to delete clip: %v", err) } return &emptypb.Empty{}, nil } func (h *GRPCHandler) LikeClip(ctx context.Context, req *proto.LikeClipRequest) (*proto.LikeClipResponse, error) { like, err := h.likeService.LikeClip(ctx, int(req.ClipId), int(req.UserId)) if err != nil { return nil, status.Errorf(codes.Internal, "failed to like clip: %v", err) } return &proto.LikeClipResponse{ Like: h.likeToProto(like), }, nil } func (h *GRPCHandler) UnlikeClip(ctx context.Context, req *proto.UnlikeClipRequest) (*emptypb.Empty, error) { err := h.likeService.UnlikeClip(ctx, int(req.ClipId), int(req.UserId)) if err != nil { return nil, status.Errorf(codes.Internal, "failed to unlike clip: %v", err) } return &emptypb.Empty{}, nil } func (h *GRPCHandler) GetClipLikes(ctx context.Context, req *proto.GetClipLikesRequest) (*proto.GetClipLikesResponse, error) { likes, totalCount, err := h.likeService.GetClipLikes(ctx, int(req.ClipId), int(req.Limit), int(req.Offset)) if err != nil { return nil, status.Errorf(codes.Internal, "failed to get clip likes: %v", err) } return &proto.GetClipLikesResponse{ Likes: h.likesToProto(likes), TotalCount: int32(totalCount), }, nil } func (h *GRPCHandler) CheckIfLiked(ctx context.Context, req *proto.CheckIfLikedRequest) (*proto.CheckIfLikedResponse, error) { isLiked, err := h.likeService.CheckIfLiked(ctx, int(req.ClipId), int(req.UserId)) if err != nil { return nil, status.Errorf(codes.Internal, "failed to check like status: %v", err) } return &proto.CheckIfLikedResponse{ IsLiked: isLiked, }, nil } func (h *GRPCHandler) CreateComment(ctx context.Context, req *proto.CreateCommentRequest) (*proto.CreateCommentResponse, error) { comment, err := h.commentService.CreateComment(ctx, int(req.ClipId), int(req.UserId), req.Content) if err != nil { return nil, status.Errorf(codes.Internal, "failed to create comment: %v", err) } return &proto.CreateCommentResponse{ Comment: h.commentToProto(comment), }, nil } func (h *GRPCHandler) GetClipComments(ctx context.Context, req *proto.GetClipCommentsRequest) (*proto.GetClipCommentsResponse, error) { comments, totalCount, err := h.commentService.GetClipComments(ctx, int(req.ClipId), int(req.Limit), int(req.Offset)) if err != nil { return nil, status.Errorf(codes.Internal, "failed to get clip comments: %v", err) } return &proto.GetClipCommentsResponse{ Comments: h.commentsToProto(comments), TotalCount: int32(totalCount), }, nil } func (h *GRPCHandler) DeleteComment(ctx context.Context, req *proto.DeleteCommentRequest) (*emptypb.Empty, error) { err := h.commentService.DeleteComment(ctx, int(req.CommentId), int(req.UserId)) if err != nil { return nil, status.Errorf(codes.Internal, "failed to delete comment: %v", err) } return &emptypb.Empty{}, nil } // Вспомогательные методы для конвертации func (h *GRPCHandler) clipToProto(clip *domain.Clip) *proto.Clip { if clip == nil { return nil } return &proto.Clip{ Id: int32(clip.ID), Title: clip.Title, VideoUrl: clip.VideoURL, ThumbnailUrl: clip.ThumbnailURL, AuthorId: int32(clip.AuthorID), LikesCount: int32(clip.LikesCount), CommentsCount: int32(clip.CommentsCount), CreatedAt: timestamppb.New(clip.CreatedAt), UpdatedAt: timestamppb.New(clip.UpdatedAt), } } func (h *GRPCHandler) clipsToProto(clips []*domain.Clip) []*proto.Clip { var protoClips []*proto.Clip for _, clip := range clips { protoClips = append(protoClips, h.clipToProto(clip)) } return protoClips } func (h *GRPCHandler) likeToProto(like *domain.ClipLike) *proto.ClipLike { if like == nil { return nil } return &proto.ClipLike{ Id: int32(like.ID), ClipId: int32(like.ClipID), UserId: int32(like.UserID), CreatedAt: timestamppb.New(like.CreatedAt), } } func (h *GRPCHandler) likesToProto(likes []*domain.ClipLike) []*proto.ClipLike { var protoLikes []*proto.ClipLike for _, like := range likes { protoLikes = append(protoLikes, h.likeToProto(like)) } return protoLikes } func (h *GRPCHandler) commentToProto(comment *domain.ClipComment) *proto.ClipComment { if comment == nil { return nil } return &proto.ClipComment{ Id: int32(comment.ID), ClipId: int32(comment.ClipID), AuthorId: int32(comment.AuthorID), Content: comment.Content, CreatedAt: timestamppb.New(comment.CreatedAt), UpdatedAt: timestamppb.New(comment.UpdatedAt), } } func (h *GRPCHandler) commentsToProto(comments []*domain.ClipComment) []*proto.ClipComment { var protoComments []*proto.ClipComment for _, comment := range comments { protoComments = append(protoComments, h.commentToProto(comment)) } return protoComments }