package useCases import ( "RenewCMS/internal/domain/hash" "RenewCMS/internal/domain/invite" "RenewCMS/internal/domain/mail" "RenewCMS/internal/domain/user" "errors" "strings" "time" ) type CreateInviteUseCase struct { userRepository user.Repository inviteRepository invite.Repository hasher hash.Hasher mailer inviteMailer } type CreateInviteCommand struct { Email string InvitedBy uint32 } func NewCreateInviteUseCase(userRepository user.Repository, inviteRepository invite.Repository, hasher hash.Hasher, mailRepository mail.Repository, publicURL string) *CreateInviteUseCase { return &CreateInviteUseCase{userRepository, inviteRepository, hasher, inviteMailer{mailRepository, publicURL}} } func (g *CreateInviteUseCase) CreateInvite(command CreateInviteCommand) (invite.Invite, error) { email := strings.ToLower(command.Email) now := time.Now() inviter, err := g.userRepository.Get(command.InvitedBy) if err != nil { return invite.Invite{}, err } if g.emailHasAccount(email) { return invite.Invite{}, invite.ErrEmailHasAccount } if err := g.discardOpenInvite(email, now); err != nil { return invite.Invite{}, err } rawCode, hashedCode, err := newInviteCode(g.hasher) if err != nil { return invite.Invite{}, err } createdInvite, err := g.inviteRepository.Create(invite.Invite{ Email: email, HashedCode: hashedCode, ExpiresAt: now.Add(invite.Lifetime), InvitedBy: command.InvitedBy, CreatedAt: now, }) if err != nil { return invite.Invite{}, err } if sendErr := g.mailer.send(email, rawCode, inviter); sendErr != nil { if deleteErr := g.inviteRepository.Delete(createdInvite.ID); deleteErr != nil { return invite.Invite{}, errors.Join(sendErr, deleteErr) } return invite.Invite{}, sendErr } return createdInvite, nil } func (g *CreateInviteUseCase) emailHasAccount(email string) bool { for _, existingUser := range g.userRepository.GetAll() { if strings.EqualFold(existingUser.Email, email) { return true } } return false } func (g *CreateInviteUseCase) discardOpenInvite(email string, now time.Time) error { for _, existing := range g.inviteRepository.GetAll() { if existing.Email != email { continue } switch existing.StatusAt(now) { case invite.StatusPending: return invite.ErrPendingInvite case invite.StatusExpired: if err := g.inviteRepository.Delete(existing.ID); err != nil { return err } } } return nil }