324 lines
10 KiB
Go
324 lines
10 KiB
Go
package grpcserver
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/XingfenD/yoresee_doc/internal/dto"
|
|
"github.com/XingfenD/yoresee_doc/internal/repository/user_repo"
|
|
"github.com/XingfenD/yoresee_doc/internal/service/auth_service"
|
|
"github.com/XingfenD/yoresee_doc/internal/service/invitation_service"
|
|
"github.com/XingfenD/yoresee_doc/internal/status"
|
|
"github.com/XingfenD/yoresee_doc/internal/utils"
|
|
pb "github.com/XingfenD/yoresee_doc/pkg/gen/yoresee_doc/v1"
|
|
)
|
|
|
|
type InvitationServiceServer struct {
|
|
pb.UnimplementedInvitationServiceServer
|
|
userRepo *user_repo.UserRepository
|
|
}
|
|
|
|
func NewInvitationServiceServer(userRepo *user_repo.UserRepository) *InvitationServiceServer {
|
|
return &InvitationServiceServer{
|
|
userRepo: userRepo,
|
|
}
|
|
}
|
|
|
|
func (s *InvitationServiceServer) requireAdmin(ctx context.Context) error {
|
|
userExternalID, ok := ctx.Value("user_external_id").(string)
|
|
if !ok || userExternalID == "" {
|
|
return status.StatusTokenInvalid
|
|
}
|
|
isAdmin, err := auth_service.AuthSvc.IsAdmin(userExternalID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !isAdmin {
|
|
return status.StatusPermissionDenied
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func parseOptionalTime(input *string) (*time.Time, error) {
|
|
if input == nil || strings.TrimSpace(*input) == "" {
|
|
return nil, nil
|
|
}
|
|
tm, err := time.Parse(time.RFC3339, *input)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &tm, nil
|
|
}
|
|
|
|
func (s *InvitationServiceServer) CreateInvitation(ctx context.Context, req *pb.CreateInvitationRequest) (*pb.CreateInvitationResponse, error) {
|
|
if req == nil {
|
|
return &pb.CreateInvitationResponse{Base: baseResponseFromStatus(status.StatusParamError)}, nil
|
|
}
|
|
if err := s.requireAdmin(ctx); err != nil {
|
|
return &pb.CreateInvitationResponse{Base: baseResponseFromErr(err)}, nil
|
|
}
|
|
|
|
userExternalID, ok := ctx.Value("user_external_id").(string)
|
|
if !ok || userExternalID == "" {
|
|
return &pb.CreateInvitationResponse{Base: baseResponseFromStatus(status.StatusTokenInvalid)}, nil
|
|
}
|
|
|
|
expiresAt, err := parseOptionalTime(req.ExpiresAt)
|
|
if err != nil {
|
|
return &pb.CreateInvitationResponse{Base: baseResponseFromStatus(status.StatusParamError)}, nil
|
|
}
|
|
|
|
inv, err := invitation_service.InvitationSvc.CreateInvitation(&dto.CreateInvitationRequest{
|
|
CreatorExternalID: userExternalID,
|
|
MaxUsedCnt: req.MaxUsedCnt,
|
|
ExpiresAt: expiresAt,
|
|
Note: req.Note,
|
|
})
|
|
if err != nil {
|
|
return &pb.CreateInvitationResponse{Base: baseResponseFromErr(err)}, nil
|
|
}
|
|
|
|
user, _ := s.userRepo.GetByExternalID(userExternalID).Exec()
|
|
resp := &dto.InvitationResponse{
|
|
Code: inv.Code,
|
|
CreatedByExternalID: userExternalID,
|
|
CreatedByName: "",
|
|
UsedCnt: inv.UsedCnt,
|
|
MaxUsedCnt: inv.MaxUsedCnt,
|
|
ExpiresAt: inv.ExpiresAt,
|
|
CreatedAt: inv.CreatedAt,
|
|
Disabled: inv.Disabled,
|
|
Note: inv.Note,
|
|
}
|
|
if user != nil {
|
|
if user.Nickname != "" {
|
|
resp.CreatedByName = user.Nickname
|
|
} else {
|
|
resp.CreatedByName = user.Username
|
|
}
|
|
}
|
|
|
|
return &pb.CreateInvitationResponse{
|
|
Base: baseResponseFromErr(nil),
|
|
Invitation: toInvitationResponse(resp),
|
|
}, nil
|
|
}
|
|
|
|
func (s *InvitationServiceServer) ListInvitations(ctx context.Context, req *pb.ListInvitationsRequest) (*pb.ListInvitationsResponse, error) {
|
|
if req == nil {
|
|
return &pb.ListInvitationsResponse{Base: baseResponseFromStatus(status.StatusParamError)}, nil
|
|
}
|
|
|
|
onlyMine := req.OnlyMine != nil && req.GetOnlyMine()
|
|
if !onlyMine {
|
|
if err := s.requireAdmin(ctx); err != nil {
|
|
return &pb.ListInvitationsResponse{Base: baseResponseFromErr(err)}, nil
|
|
}
|
|
}
|
|
|
|
var creatorID *int64
|
|
if onlyMine {
|
|
userExternalID, ok := ctx.Value("user_external_id").(string)
|
|
if !ok || strings.TrimSpace(userExternalID) == "" {
|
|
return &pb.ListInvitationsResponse{Base: baseResponseFromStatus(status.StatusTokenInvalid)}, nil
|
|
}
|
|
id, err := s.userRepo.GetIDByExternalID(userExternalID).Exec()
|
|
if err != nil {
|
|
return &pb.ListInvitationsResponse{Base: baseResponseFromErr(status.StatusUserNotFound)}, nil
|
|
}
|
|
creatorID = utils.Of(id)
|
|
}
|
|
|
|
sortArgs := dto.SortArgs{Field: "created_at", Desc: true}
|
|
if req.OrderBy != nil {
|
|
sortArgs.Field = req.GetOrderBy()
|
|
}
|
|
if req.OrderDesc != nil {
|
|
sortArgs.Desc = req.GetOrderDesc()
|
|
}
|
|
|
|
listReq := &dto.ListInvitationsReq{
|
|
CreatorID: creatorID,
|
|
MaxUsedCnt: req.MaxUsedCnt,
|
|
ExpiresAtStart: req.ExpiresAtStart,
|
|
ExpiresAtEnd: req.ExpiresAtEnd,
|
|
CreatedAtStart: req.CreatedAtStart,
|
|
CreatedAtEnd: req.CreatedAtEnd,
|
|
Disabled: req.Disabled,
|
|
OnlyMine: req.OnlyMine,
|
|
Keyword: req.Keyword,
|
|
SortArgs: sortArgs,
|
|
Pagination: dto.Pagination{
|
|
Page: int(req.Page),
|
|
PageSize: int(req.PageSize),
|
|
},
|
|
}
|
|
|
|
invitations, total, err := invitation_service.InvitationSvc.ListInvitations(listReq)
|
|
if err != nil {
|
|
return &pb.ListInvitationsResponse{Base: baseResponseFromErr(err)}, nil
|
|
}
|
|
|
|
creatorIDs := make([]int64, 0, len(invitations))
|
|
for _, inv := range invitations {
|
|
creatorIDs = append(creatorIDs, inv.CreatedBy)
|
|
}
|
|
creatorMap, _ := s.userRepo.MGetUserByID(creatorIDs).Exec()
|
|
|
|
respInvites := make([]*pb.InvitationResponse, 0, len(invitations))
|
|
for _, inv := range invitations {
|
|
createdByExternalID := ""
|
|
createdByName := ""
|
|
if creator := creatorMap[inv.CreatedBy]; creator != nil {
|
|
createdByExternalID = creator.ExternalID
|
|
if creator.Nickname != "" {
|
|
createdByName = creator.Nickname
|
|
} else {
|
|
createdByName = creator.Username
|
|
}
|
|
}
|
|
resp := &dto.InvitationResponse{
|
|
Code: inv.Code,
|
|
CreatedByExternalID: createdByExternalID,
|
|
CreatedByName: createdByName,
|
|
UsedCnt: inv.UsedCnt,
|
|
MaxUsedCnt: inv.MaxUsedCnt,
|
|
ExpiresAt: inv.ExpiresAt,
|
|
CreatedAt: inv.CreatedAt,
|
|
Disabled: inv.Disabled,
|
|
Note: inv.Note,
|
|
}
|
|
respInvites = append(respInvites, toInvitationResponse(resp))
|
|
}
|
|
|
|
return &pb.ListInvitationsResponse{
|
|
Base: baseResponseFromErr(nil),
|
|
Invitations: respInvites,
|
|
Total: total,
|
|
}, nil
|
|
}
|
|
|
|
func (s *InvitationServiceServer) UpdateInvitation(ctx context.Context, req *pb.UpdateInvitationRequest) (*pb.UpdateInvitationResponse, error) {
|
|
if req == nil || strings.TrimSpace(req.Code) == "" {
|
|
return &pb.UpdateInvitationResponse{Base: baseResponseFromStatus(status.StatusParamError)}, nil
|
|
}
|
|
if err := s.requireAdmin(ctx); err != nil {
|
|
return &pb.UpdateInvitationResponse{Base: baseResponseFromErr(err)}, nil
|
|
}
|
|
|
|
expiresAt, err := parseOptionalTime(req.ExpiresAt)
|
|
if err != nil {
|
|
return &pb.UpdateInvitationResponse{Base: baseResponseFromStatus(status.StatusParamError)}, nil
|
|
}
|
|
|
|
if err := invitation_service.InvitationSvc.UpdateInvitation(&dto.UpdateInvitationRequest{
|
|
Code: req.Code,
|
|
MaxUsedCnt: req.MaxUsedCnt,
|
|
ExpiresAt: expiresAt,
|
|
Disabled: req.Disabled,
|
|
Note: req.Note,
|
|
}); err != nil {
|
|
return &pb.UpdateInvitationResponse{Base: baseResponseFromErr(err)}, nil
|
|
}
|
|
|
|
return &pb.UpdateInvitationResponse{Base: baseResponseFromErr(nil)}, nil
|
|
}
|
|
|
|
func (s *InvitationServiceServer) DeleteInvitation(ctx context.Context, req *pb.DeleteInvitationRequest) (*pb.DeleteInvitationResponse, error) {
|
|
if req == nil || strings.TrimSpace(req.Code) == "" {
|
|
return &pb.DeleteInvitationResponse{Base: baseResponseFromStatus(status.StatusParamError)}, nil
|
|
}
|
|
if err := s.requireAdmin(ctx); err != nil {
|
|
return &pb.DeleteInvitationResponse{Base: baseResponseFromErr(err)}, nil
|
|
}
|
|
|
|
if err := invitation_service.InvitationSvc.DeleteInvitation(&dto.DeleteInvitationRequest{Code: req.Code}); err != nil {
|
|
return &pb.DeleteInvitationResponse{Base: baseResponseFromErr(err)}, nil
|
|
}
|
|
|
|
return &pb.DeleteInvitationResponse{Base: baseResponseFromErr(nil)}, nil
|
|
}
|
|
|
|
func (s *InvitationServiceServer) ListInvitationRecords(ctx context.Context, req *pb.ListInvitationRecordsRequest) (*pb.ListInvitationRecordsResponse, error) {
|
|
if req == nil {
|
|
return &pb.ListInvitationRecordsResponse{Base: baseResponseFromStatus(status.StatusParamError)}, nil
|
|
}
|
|
onlyMine := req.OnlyMine != nil && req.GetOnlyMine()
|
|
if !onlyMine {
|
|
if err := s.requireAdmin(ctx); err != nil {
|
|
return &pb.ListInvitationRecordsResponse{Base: baseResponseFromErr(err)}, nil
|
|
}
|
|
}
|
|
|
|
var creatorID *int64
|
|
if onlyMine {
|
|
userExternalID, ok := ctx.Value("user_external_id").(string)
|
|
if !ok || strings.TrimSpace(userExternalID) == "" {
|
|
return &pb.ListInvitationRecordsResponse{Base: baseResponseFromStatus(status.StatusTokenInvalid)}, nil
|
|
}
|
|
id, err := s.userRepo.GetIDByExternalID(userExternalID).Exec()
|
|
if err != nil {
|
|
return &pb.ListInvitationRecordsResponse{Base: baseResponseFromErr(status.StatusUserNotFound)}, nil
|
|
}
|
|
creatorID = utils.Of(id)
|
|
}
|
|
|
|
listReq := &dto.ListInvitationRecordsRequest{
|
|
Code: req.Code,
|
|
Status: req.Status,
|
|
UsedAtStart: req.UsedAtStart,
|
|
UsedAtEnd: req.UsedAtEnd,
|
|
CreatorID: creatorID,
|
|
OnlyMine: req.OnlyMine,
|
|
Keyword: req.Keyword,
|
|
Pagination: dto.Pagination{
|
|
Page: int(req.Page),
|
|
PageSize: int(req.PageSize),
|
|
},
|
|
}
|
|
|
|
records, total, err := invitation_service.InvitationSvc.ListInvitationRecords(listReq)
|
|
if err != nil {
|
|
return &pb.ListInvitationRecordsResponse{Base: baseResponseFromErr(err)}, nil
|
|
}
|
|
|
|
userIDs := make([]int64, 0, len(records))
|
|
for _, r := range records {
|
|
if r.UsedByUserID != nil {
|
|
userIDs = append(userIDs, *r.UsedByUserID)
|
|
}
|
|
}
|
|
userMap, _ := s.userRepo.MGetUserByID(userIDs).Exec()
|
|
|
|
respRecords := make([]*pb.InvitationRecordResponse, 0, len(records))
|
|
for _, r := range records {
|
|
usedBy := r.UsedBy
|
|
usedByExternalID := ""
|
|
if r.UsedByUserID != nil {
|
|
if user := userMap[*r.UsedByUserID]; user != nil {
|
|
usedByExternalID = user.ExternalID
|
|
if user.Nickname != "" {
|
|
usedBy = user.Nickname
|
|
} else if user.Username != "" {
|
|
usedBy = user.Username
|
|
}
|
|
}
|
|
}
|
|
resp := &dto.InvitationRecordResponse{
|
|
Code: r.Code,
|
|
UsedBy: usedBy,
|
|
UsedByExternalID: usedByExternalID,
|
|
UsedAt: r.UsedAt,
|
|
Status: r.Status,
|
|
}
|
|
respRecords = append(respRecords, toInvitationRecordResponse(resp))
|
|
}
|
|
|
|
return &pb.ListInvitationRecordsResponse{
|
|
Base: baseResponseFromErr(nil),
|
|
Records: respRecords,
|
|
Total: total,
|
|
}, nil
|
|
}
|