feat: Authors feature backend (and upgraded deprecated mods)
This commit is contained in:
1 parent
a11338094e
commit
b34a173d83
103 files changed
+6454
-240
No files matched your search
@@ -0,0 +1,152 @@
|
||||
package useCases
|
||||
|
||||
import (
|
||||
"RenewCMS/internal/domain/audit"
|
||||
"RenewCMS/internal/domain/author"
|
||||
"RenewCMS/internal/domain/image"
|
||||
"RenewCMS/internal/domain/user"
|
||||
"context"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type UpdateAuthorUseCase struct {
|
||||
authors author.Repository
|
||||
users user.Repository
|
||||
images image.Repository
|
||||
recorder audit.Recorder
|
||||
}
|
||||
|
||||
func NewUpdateAuthorUseCase(authors author.Repository, users user.Repository, images image.Repository, recorder audit.Recorder) *UpdateAuthorUseCase {
|
||||
return &UpdateAuthorUseCase{authors: authors, users: users, images: images, recorder: recorder}
|
||||
}
|
||||
|
||||
func (u *UpdateAuthorUseCase) Update(ctx context.Context, id uint32, caller AuthorCaller, patch author.Patch) (author.Author, error) {
|
||||
before, err := u.authors.Get(id)
|
||||
if err != nil {
|
||||
return author.Author{}, err
|
||||
}
|
||||
|
||||
event := audit.NewEvent(audit.ActionAuthorEdit, audit.CategoryAuthors)
|
||||
event.Target = authorTarget(before)
|
||||
after, err := u.prepare(before, caller, patch)
|
||||
if err != nil {
|
||||
recordFailure(ctx, u.recorder, event, err)
|
||||
return author.Author{}, err
|
||||
}
|
||||
|
||||
changes := authorChanges(u.users, u.images, before, after)
|
||||
if len(changes) == 0 {
|
||||
return u.present(before, caller), nil
|
||||
}
|
||||
saved, err := u.authors.Update(after)
|
||||
if err != nil {
|
||||
recordFailure(ctx, u.recorder, event, err)
|
||||
return author.Author{}, err
|
||||
}
|
||||
event.Target = authorTarget(saved)
|
||||
event.Changes = changes
|
||||
u.recorder.Record(ctx, event)
|
||||
return u.present(saved, caller), nil
|
||||
}
|
||||
|
||||
func (u *UpdateAuthorUseCase) present(a author.Author, caller AuthorCaller) author.Author {
|
||||
if caller.isAdmin() {
|
||||
return withUserEmail(u.users, a)
|
||||
}
|
||||
a.UserEmail = nil
|
||||
return a
|
||||
}
|
||||
|
||||
func (u *UpdateAuthorUseCase) prepare(before author.Author, caller AuthorCaller, patch author.Patch) (author.Author, error) {
|
||||
if err := authorizeEdit(before, caller, patch); err != nil {
|
||||
return author.Author{}, err
|
||||
}
|
||||
after := before
|
||||
if patch.Name != nil {
|
||||
after.Name = strings.TrimSpace(*patch.Name)
|
||||
}
|
||||
if patch.Bio != nil {
|
||||
after.Bio = *patch.Bio
|
||||
}
|
||||
if patch.Website.Set {
|
||||
after.Website = patch.Website.Value
|
||||
}
|
||||
if patch.SocialLinks != nil {
|
||||
after.SocialLinks = *patch.SocialLinks
|
||||
}
|
||||
website, err := validateAuthorContent(after.Bio, after.Website, after.SocialLinks)
|
||||
if err != nil {
|
||||
return author.Author{}, err
|
||||
}
|
||||
after.Website = website
|
||||
if err := u.applySlug(&after, patch); err != nil {
|
||||
return author.Author{}, err
|
||||
}
|
||||
if err := u.applyAvatar(&after, patch); err != nil {
|
||||
return author.Author{}, err
|
||||
}
|
||||
return after, u.applyLink(&after, patch)
|
||||
}
|
||||
|
||||
func authorizeEdit(before author.Author, caller AuthorCaller, patch author.Patch) error {
|
||||
if caller.isAdmin() {
|
||||
return nil
|
||||
}
|
||||
if before.UserID == nil || *before.UserID != caller.UserID || patch.UserID.Set {
|
||||
return author.ErrAuthorRights
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *UpdateAuthorUseCase) applySlug(after *author.Author, patch author.Patch) error {
|
||||
if patch.Slug == nil || *patch.Slug == after.Slug {
|
||||
return nil
|
||||
}
|
||||
if !author.IsValidSlug(*patch.Slug) {
|
||||
return author.ErrInvalidSlug
|
||||
}
|
||||
taken, err := isSlugTaken(u.authors, *patch.Slug)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if taken {
|
||||
return author.ErrSlugTaken
|
||||
}
|
||||
after.Slug = *patch.Slug
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *UpdateAuthorUseCase) applyAvatar(after *author.Author, patch author.Patch) error {
|
||||
if !patch.AvatarID.Set {
|
||||
return nil
|
||||
}
|
||||
if err := ensureImageExists(u.images, patch.AvatarID.Value); err != nil {
|
||||
return err
|
||||
}
|
||||
after.AvatarID = patch.AvatarID.Value
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *UpdateAuthorUseCase) applyLink(after *author.Author, patch author.Patch) error {
|
||||
if !patch.UserID.Set {
|
||||
return nil
|
||||
}
|
||||
requested := patch.UserID.Value
|
||||
if requested == nil {
|
||||
after.UserID = nil
|
||||
after.IsLinked = false
|
||||
return nil
|
||||
}
|
||||
if after.UserID != nil {
|
||||
if *after.UserID == *requested {
|
||||
return nil
|
||||
}
|
||||
return author.ErrAuthorAlreadyLinked
|
||||
}
|
||||
if err := ensureUserCanBeLinked(u.users, u.authors, *requested); err != nil {
|
||||
return err
|
||||
}
|
||||
after.UserID = requested
|
||||
after.IsLinked = true
|
||||
return nil
|
||||
}
|
||||
Reference in new issue
Block a user