package useCases import ( "RenewCMS/internal/domain/article" "RenewCMS/internal/domain/audit" "RenewCMS/internal/domain/author" "RenewCMS/internal/domain/image" "RenewCMS/internal/domain/purge" "RenewCMS/internal/domain/site" "context" "log" "slices" "sync" "time" ) type RunPurgeUseCase struct { planner *purgePlanner merge *MergeMediaUseCase repository purge.Repository now func() time.Time logger *log.Logger recorder audit.Recorder guard sync.Mutex } func NewRunPurgeUseCase(imageRepository image.Repository, articleRepository article.Repository, merge *MergeMediaUseCase, repository purge.Repository, now func() time.Time, logger *log.Logger, recorder audit.Recorder, sites site.Repository, authors author.Repository) *RunPurgeUseCase { return &RunPurgeUseCase{ planner: &purgePlanner{imageRepository: imageRepository, articleRepository: articleRepository, sites: sites, authors: authors}, merge: merge, repository: repository, now: now, logger: logger, recorder: recorder, } } func (u *RunPurgeUseCase) Run(ctx context.Context, fingerprint *string) (purge.RunResult, error) { result, err := u.Execute(fingerprint) event := audit.NewEvent(audit.ActionMediaPurge, audit.CategoryMedia) if err != nil { recordFailure(ctx, u.recorder, event, err) return purge.RunResult{}, err } event.Params = purgeParams(result) u.recorder.Record(ctx, event) return result, nil } func purgeParams(result purge.RunResult) map[string]any { params := map[string]any{audit.ParamMerged: result.Merged, audit.ParamArticlesUpdated: result.ArticlesUpdated} if result.FailedGroups > 0 { params[audit.ParamFailedGroups] = result.FailedGroups } return params } func (u *RunPurgeUseCase) Execute(fingerprint *string) (purge.RunResult, error) { if !u.guard.TryLock() { return purge.RunResult{}, purge.ErrPurgeBusy } defer u.guard.Unlock() plan, err := u.planner.plan() if err != nil { return purge.RunResult{}, err } if fingerprint != nil && *fingerprint != plan.fingerprint { return purge.RunResult{}, purge.ErrPurgeConflict } result := u.mergeGroups(plan) if err := u.recordRun(result); err != nil { u.logger.Printf("purge: could not record the run result: %v", err) return purge.RunResult{}, err } return result, nil } func (u *RunPurgeUseCase) mergeGroups(plan purgePlan) purge.RunResult { result := purge.RunResult{GroupsTotal: len(plan.groups)} updated := map[uint32]bool{} for _, group := range plan.groups { merged, err := u.merge.merge(group.kept.ID, slices.Clone(group.mergedIds)) if err != nil { result.FailedGroups++ continue } result.GroupsPurged++ result.Merged += merged.Merged result.SitesUpdated += merged.SitesUpdated result.AuthorsUpdated += merged.AuthorsUpdated for _, id := range merged.UpdatedArticleIDs { updated[id] = true } } result.ArticlesUpdated = len(updated) return result } func (u *RunPurgeUseCase) recordRun(result purge.RunResult) error { return u.repository.SaveLastRun(u.now().UTC(), result.Merged, result.ArticlesUpdated) }