feat(API): media library
This commit is contained in:
1 parent
95ae043d3e
commit
0d99544579
65 files changed
+3992
-1530
No files matched your search
@@ -0,0 +1,137 @@
|
||||
package useCases
|
||||
|
||||
import (
|
||||
"RenewCMS/internal/domain/article"
|
||||
"RenewCMS/internal/domain/image"
|
||||
"cmp"
|
||||
"errors"
|
||||
"slices"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type MergeMediaResult struct {
|
||||
ArticlesUpdated int
|
||||
Merged int
|
||||
}
|
||||
|
||||
type MergeMediaUseCase struct {
|
||||
imageRepository image.Repository
|
||||
articleRepository article.Repository
|
||||
}
|
||||
|
||||
func NewMergeMediaUseCase(imageRepository image.Repository, articleRepository article.Repository) *MergeMediaUseCase {
|
||||
return &MergeMediaUseCase{imageRepository, articleRepository}
|
||||
}
|
||||
|
||||
type bodyRewrite struct {
|
||||
articleId uint32
|
||||
original string
|
||||
rewritten string
|
||||
}
|
||||
|
||||
func (u *MergeMediaUseCase) Merge(keepId uint32, mergeIds []uint32) (MergeMediaResult, error) {
|
||||
if err := validateMergeRequest(keepId, mergeIds); err != nil {
|
||||
return MergeMediaResult{}, err
|
||||
}
|
||||
|
||||
kept, err := u.imageRepository.Get(keepId)
|
||||
if err != nil {
|
||||
return MergeMediaResult{}, err
|
||||
}
|
||||
copies, err := u.loadCopies(kept, mergeIds)
|
||||
if err != nil {
|
||||
return MergeMediaResult{}, err
|
||||
}
|
||||
|
||||
rewrites, err := u.planRewrites(kept, copies)
|
||||
if err != nil {
|
||||
return MergeMediaResult{}, err
|
||||
}
|
||||
if err := u.applyRewrites(rewrites); err != nil {
|
||||
return MergeMediaResult{}, err
|
||||
}
|
||||
|
||||
for _, duplicate := range copies {
|
||||
if err := u.imageRepository.Delete(duplicate.ID); err != nil {
|
||||
return MergeMediaResult{}, err
|
||||
}
|
||||
}
|
||||
|
||||
return MergeMediaResult{ArticlesUpdated: len(rewrites), Merged: len(copies)}, nil
|
||||
}
|
||||
|
||||
func validateMergeRequest(keepId uint32, mergeIds []uint32) error {
|
||||
if keepId == 0 || len(mergeIds) == 0 {
|
||||
return image.ErrInvalidMergeRequest
|
||||
}
|
||||
seen := map[uint32]bool{keepId: true}
|
||||
for _, id := range mergeIds {
|
||||
if id == 0 || seen[id] {
|
||||
return image.ErrInvalidMergeRequest
|
||||
}
|
||||
seen[id] = true
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *MergeMediaUseCase) loadCopies(kept image.Image, mergeIds []uint32) ([]image.Image, error) {
|
||||
copies := make([]image.Image, 0, len(mergeIds))
|
||||
for _, id := range mergeIds {
|
||||
duplicate, err := u.imageRepository.Get(id)
|
||||
if errors.Is(err, image.ErrImageNotFound) {
|
||||
return nil, image.ErrMergeCopyMissing
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if duplicate.SHA256 == "" || duplicate.SHA256 != kept.SHA256 {
|
||||
return nil, image.ErrMergeHashMismatch
|
||||
}
|
||||
copies = append(copies, duplicate)
|
||||
}
|
||||
return copies, nil
|
||||
}
|
||||
|
||||
func (u *MergeMediaUseCase) planRewrites(kept image.Image, copies []image.Image) ([]bodyRewrite, error) {
|
||||
keptName := image.StoredName(kept.Path)
|
||||
rewritten := map[uint32]*bodyRewrite{}
|
||||
|
||||
for _, duplicate := range copies {
|
||||
copyName := image.StoredName(duplicate.Path)
|
||||
articles, err := u.articleRepository.FindByBodyContaining(copyName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, a := range articles {
|
||||
plan, ok := rewritten[a.ID]
|
||||
if !ok {
|
||||
plan = &bodyRewrite{articleId: a.ID, original: a.Body, rewritten: a.Body}
|
||||
rewritten[a.ID] = plan
|
||||
}
|
||||
plan.rewritten = strings.ReplaceAll(plan.rewritten, copyName, keptName)
|
||||
}
|
||||
}
|
||||
|
||||
plans := make([]bodyRewrite, 0, len(rewritten))
|
||||
for _, plan := range rewritten {
|
||||
plans = append(plans, *plan)
|
||||
}
|
||||
slices.SortFunc(plans, func(a, b bodyRewrite) int { return cmp.Compare(a.articleId, b.articleId) })
|
||||
return plans, nil
|
||||
}
|
||||
|
||||
func (u *MergeMediaUseCase) applyRewrites(rewrites []bodyRewrite) error {
|
||||
for applied, plan := range rewrites {
|
||||
if _, err := u.articleRepository.UpdateBody(plan.articleId, plan.rewritten); err != nil {
|
||||
u.restoreBodies(rewrites[:applied])
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *MergeMediaUseCase) restoreBodies(applied []bodyRewrite) {
|
||||
for _, plan := range applied {
|
||||
_, _ = u.articleRepository.UpdateBody(plan.articleId, plan.original)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user