86 lines
2.5 KiB
Go
86 lines
2.5 KiB
Go
package useCases
|
|
|
|
import (
|
|
"RenewCMS/internal/domain/audit"
|
|
"RenewCMS/internal/domain/author"
|
|
"RenewCMS/internal/domain/user"
|
|
"context"
|
|
"errors"
|
|
"strings"
|
|
)
|
|
|
|
const maxOwnSlugAttempts = 50
|
|
|
|
type MyAuthorCreated struct {
|
|
Author author.Author
|
|
SlugAdjusted bool
|
|
}
|
|
|
|
type CreateMyAuthorUseCase struct {
|
|
authors author.Repository
|
|
users user.Repository
|
|
recorder audit.Recorder
|
|
}
|
|
|
|
func NewCreateMyAuthorUseCase(authors author.Repository, users user.Repository, recorder audit.Recorder) *CreateMyAuthorUseCase {
|
|
return &CreateMyAuthorUseCase{authors: authors, users: users, recorder: recorder}
|
|
}
|
|
|
|
func (u *CreateMyAuthorUseCase) Create(ctx context.Context, userID uint32) (MyAuthorCreated, error) {
|
|
caller, err := u.users.Get(userID)
|
|
if err != nil {
|
|
return MyAuthorCreated{}, err
|
|
}
|
|
name := authorNameOf(caller)
|
|
event := audit.NewEvent(audit.ActionAuthorCreate, audit.CategoryAuthors)
|
|
event.Target = audit.Target{Type: audit.TargetAuthor, Name: name}
|
|
created, err := u.create(caller, name)
|
|
if err != nil {
|
|
recordFailure(ctx, u.recorder, event, err)
|
|
return MyAuthorCreated{}, err
|
|
}
|
|
event.Target = authorTarget(created)
|
|
u.recorder.Record(ctx, event)
|
|
return MyAuthorCreated{Author: created, SlugAdjusted: created.Slug != author.GenerateSlug(name)}, nil
|
|
}
|
|
|
|
func authorNameOf(caller user.User) string {
|
|
name := strings.TrimSpace(caller.DisplayName)
|
|
if name == "" {
|
|
name = strings.TrimSpace(caller.Username)
|
|
}
|
|
runes := []rune(name)
|
|
return string(runes[:min(len(runes), author.MaxNameRunes)])
|
|
}
|
|
|
|
func (u *CreateMyAuthorUseCase) create(caller user.User, name string) (author.Author, error) {
|
|
_, err := u.authors.GetByUserID(caller.ID)
|
|
if err == nil {
|
|
return author.Author{}, author.ErrUserAlreadyLinked
|
|
}
|
|
if !errors.Is(err, author.ErrAuthorNotFound) {
|
|
return author.Author{}, err
|
|
}
|
|
return u.createWithFreeSlug(caller.ID, name)
|
|
}
|
|
|
|
func (u *CreateMyAuthorUseCase) createWithFreeSlug(userID uint32, name string) (author.Author, error) {
|
|
base := author.GenerateSlug(name)
|
|
for attempt := 1; attempt <= maxOwnSlugAttempts; attempt++ {
|
|
slug := author.SlugWithSuffix(base, attempt)
|
|
taken, err := isSlugTaken(u.authors, slug)
|
|
if err != nil {
|
|
return author.Author{}, err
|
|
}
|
|
if taken {
|
|
continue
|
|
}
|
|
created, err := u.authors.Create(author.Author{Name: name, Slug: slug, SocialLinks: []author.SocialLink{}, UserID: &userID, IsLinked: true})
|
|
if errors.Is(err, author.ErrSlugTaken) {
|
|
continue
|
|
}
|
|
return created, err
|
|
}
|
|
return author.Author{}, author.ErrSlugTaken
|
|
}
|