feat: invitation accept screen and api
This commit is contained in:
1 parent
310cfd528e
commit
55525c06d6
23 files changed
+2043
-11
No files matched your search
@@ -0,0 +1,87 @@
|
||||
package useCases
|
||||
|
||||
import (
|
||||
"RenewCMS/internal/domain/hash"
|
||||
"RenewCMS/internal/domain/invite"
|
||||
"RenewCMS/internal/domain/user"
|
||||
"errors"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type AcceptInviteUseCase struct {
|
||||
userRepository user.Repository
|
||||
inviteRepository invite.Repository
|
||||
hasher hash.Hasher
|
||||
}
|
||||
|
||||
type AcceptInviteCommand struct {
|
||||
Code string
|
||||
Username string
|
||||
Password string
|
||||
}
|
||||
|
||||
func NewAcceptInviteUseCase(userRepository user.Repository, inviteRepository invite.Repository, hasher hash.Hasher) *AcceptInviteUseCase {
|
||||
return &AcceptInviteUseCase{userRepository, inviteRepository, hasher}
|
||||
}
|
||||
|
||||
func (a *AcceptInviteUseCase) AcceptInvite(command AcceptInviteCommand) (user.User, error) {
|
||||
now := time.Now()
|
||||
|
||||
pending, err := findUsableInvite(a.inviteRepository, a.hasher, command.Code, now)
|
||||
if err != nil {
|
||||
return user.User{}, err
|
||||
}
|
||||
|
||||
if a.emailHasAccount(pending.Email) {
|
||||
return user.User{}, invite.ErrEmailHasAccount
|
||||
}
|
||||
|
||||
if err := a.ensureUsernameFree(command.Username); err != nil {
|
||||
return user.User{}, err
|
||||
}
|
||||
|
||||
hashedPassword, err := a.hasher.Hash(command.Password)
|
||||
if err != nil {
|
||||
return user.User{}, err
|
||||
}
|
||||
|
||||
createdUser, err := a.userRepository.Create(user.User{
|
||||
Username: command.Username,
|
||||
Password: hashedPassword,
|
||||
Email: pending.Email,
|
||||
IsVerified: true,
|
||||
})
|
||||
if err != nil {
|
||||
return user.User{}, err
|
||||
}
|
||||
|
||||
if err := a.inviteRepository.MarkAccepted(pending.ID, now); err != nil {
|
||||
if deleteErr := a.userRepository.Delete(createdUser.ID); deleteErr != nil {
|
||||
return user.User{}, errors.Join(err, deleteErr)
|
||||
}
|
||||
return user.User{}, err
|
||||
}
|
||||
|
||||
return createdUser, nil
|
||||
}
|
||||
|
||||
func (a *AcceptInviteUseCase) emailHasAccount(email string) bool {
|
||||
for _, existingUser := range a.userRepository.GetAll() {
|
||||
if strings.EqualFold(existingUser.Email, email) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (a *AcceptInviteUseCase) ensureUsernameFree(username string) error {
|
||||
_, err := a.userRepository.GetByUsername(username)
|
||||
if err == nil {
|
||||
return user.ErrUsernameTaken
|
||||
}
|
||||
if errors.Is(err, user.ErrUserNotFound) {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package useCases
|
||||
|
||||
import (
|
||||
"RenewCMS/internal/domain/hash"
|
||||
"RenewCMS/internal/domain/invite"
|
||||
"time"
|
||||
)
|
||||
|
||||
type GetInviteUseCase struct {
|
||||
inviteRepository invite.Repository
|
||||
hasher hash.Hasher
|
||||
}
|
||||
|
||||
func NewGetInviteUseCase(inviteRepository invite.Repository, hasher hash.Hasher) *GetInviteUseCase {
|
||||
return &GetInviteUseCase{inviteRepository, hasher}
|
||||
}
|
||||
|
||||
func (g *GetInviteUseCase) GetInvite(code string) (invite.Invite, error) {
|
||||
return findUsableInvite(g.inviteRepository, g.hasher, code, time.Now())
|
||||
}
|
||||
|
||||
func findUsableInvite(inviteRepository invite.Repository, hasher hash.Hasher, code string, now time.Time) (invite.Invite, error) {
|
||||
if code == "" {
|
||||
return invite.Invite{}, invite.ErrInviteInvalid
|
||||
}
|
||||
|
||||
for _, candidate := range inviteRepository.GetAll() {
|
||||
if candidate.AcceptedAt != nil {
|
||||
continue
|
||||
}
|
||||
if hasher.Compare(candidate.HashedCode, code) != nil {
|
||||
continue
|
||||
}
|
||||
if candidate.StatusAt(now) == invite.StatusExpired {
|
||||
return invite.Invite{}, invite.ErrInviteExpired
|
||||
}
|
||||
return candidate, nil
|
||||
}
|
||||
|
||||
return invite.Invite{}, invite.ErrInviteInvalid
|
||||
}
|
||||
Reference in new issue
Block a user