Files
RenewCMS/internal/infrastructure/useCases/CreateUser.go
T
2026-10-06 22:25:23 +02:00

112 lines
2.9 KiB
Go

package useCases
import (
"RenewCMS/internal/domain/audit"
"RenewCMS/internal/domain/hash"
"RenewCMS/internal/domain/mail"
"RenewCMS/internal/domain/user"
"context"
"errors"
"fmt"
"time"
"github.com/google/uuid"
)
type CreateUserUseCase struct {
userRepository user.Repository
hasher hash.Hasher
mailRepository mail.Repository
publicURL string
recorder audit.Recorder
}
type CreateUserCommand struct {
Username string
Password string
Email string
}
type MailValidationData struct {
Host string
VerificationCode string
}
func NewCreateUserUseCase(userRepository user.Repository, hasher hash.Hasher, mailRepository mail.Repository, publicURL string, recorder audit.Recorder) *CreateUserUseCase {
return &CreateUserUseCase{userRepository, hasher, mailRepository, publicURL, recorder}
}
func (g *CreateUserUseCase) CreateUser(ctx context.Context, createUser CreateUserCommand) (user.User, error) {
event := audit.NewEvent(audit.ActionUserRegister, audit.CategoryUsers)
created, err := g.create(createUser)
if err != nil {
event.Actor = audit.AnonymousActor(createUser.Username)
recordFailure(ctx, g.recorder, event, err)
return user.User{}, err
}
event.Actor = audit.UserActor(created)
event.Target = audit.NewTarget(audit.TargetUser, created.ID, created.Username)
g.recorder.Record(ctx, event)
return created, nil
}
func (g *CreateUserUseCase) create(createUser CreateUserCommand) (user.User, error) {
if g.registrationClosed() {
return user.User{}, user.ErrRegistrationClosed
}
rawUuid := uuid.NewString()
hashedPassword, err := g.hasher.Hash(createUser.Password)
if err != nil {
return user.User{}, err
}
hashedVerificationCode, err := g.hasher.Hash(rawUuid)
if err != nil {
return user.User{}, err
}
createdUser, err := g.userRepository.Create(user.User{
Username: createUser.Username,
Password: hashedPassword,
PasswordResetCode: "",
Email: createUser.Email,
IsVerified: false,
Role: user.RoleOwner,
VerificationCode: hashedVerificationCode,
VerificationExpiration: time.Now().Add(2 * time.Hour),
})
if err != nil {
return user.User{}, err
}
createdUser.VerificationCode = rawUuid
if err := g.mailRepository.Send(createdUser.Email, "verification", MailValidationData{
Host: g.publicURL,
VerificationCode: rawUuid,
}); err != nil {
sendErr := fmt.Errorf("%w: %w", user.ErrVerificationEmailNotSent, err)
if deleteErr := g.userRepository.Delete(createdUser.ID); deleteErr != nil {
return user.User{}, errors.Join(sendErr, deleteErr)
}
return user.User{}, sendErr
}
return createdUser, nil
}
func (g *CreateUserUseCase) IsRegistrationOpen() bool {
return !g.registrationClosed()
}
func (g *CreateUserUseCase) registrationClosed() bool {
for _, existingUser := range g.userRepository.GetAll() {
if existingUser.IsVerified {
return true
}
}
return false
}