package useCases import ( "RenewCMS/internal/domain/article" "RenewCMS/internal/domain/author" "RenewCMS/internal/domain/image" "RenewCMS/internal/domain/site" "slices" "strings" ) const ( UsageTypeArticle = "article" UsageTypeAuthor = "author" UsageTypeSite = "site" UsageFieldBody = "body" UsageFieldCover = "cover" UsageFieldSocial = "social" UsageFieldAvatar = "avatar" UsageFieldDefaultSocialImage = "default_social_image" ) type MediaUsage struct { Type string ID uint32 Title string Published *bool Fields []string } type GetMediaUsageUseCase struct { imageRepository image.Repository articleRepository article.Repository sites site.Repository authors author.Repository } func NewGetMediaUsageUseCase(imageRepository image.Repository, articleRepository article.Repository, sites site.Repository, authors author.Repository) *GetMediaUsageUseCase { return &GetMediaUsageUseCase{imageRepository: imageRepository, articleRepository: articleRepository, sites: sites, authors: authors} } func (u *GetMediaUsageUseCase) Usage(id uint32) ([]MediaUsage, error) { img, err := u.imageRepository.Get(id) if err != nil { return nil, err } usages, err := u.articleUsages(img) if err != nil { return nil, err } authorUsages, err := u.authorUsages(img) if err != nil { return nil, err } siteUsages, err := u.siteUsages(img) if err != nil { return nil, err } return slices.Concat(usages, authorUsages, siteUsages), nil } func (u *GetMediaUsageUseCase) articleUsages(img image.Image) ([]MediaUsage, error) { storedName := image.StoredName(img.Path) inBody, err := u.articleRepository.FindByBodyContaining(storedName) if err != nil { return nil, err } referencing, err := u.articleRepository.FindByImage(img.ID) if err != nil { return nil, err } byID := map[uint32]*MediaUsage{} for _, found := range inBody { if !strings.Contains(found.Body, storedName) { continue } entry := usageFor(byID, found) entry.Fields = append(entry.Fields, UsageFieldBody) } for _, found := range referencing { entry := usageFor(byID, found) if found.CoverImageID != nil && *found.CoverImageID == img.ID { entry.Fields = append(entry.Fields, UsageFieldCover) } if found.SocialImageID != nil && *found.SocialImageID == img.ID { entry.Fields = append(entry.Fields, UsageFieldSocial) } } usages := make([]MediaUsage, 0, len(byID)) for _, entry := range byID { usages = append(usages, *entry) } slices.SortFunc(usages, func(a, b MediaUsage) int { return int(a.ID) - int(b.ID) }) return usages, nil } func usageFor(byID map[uint32]*MediaUsage, found article.Article) *MediaUsage { if entry, ok := byID[found.ID]; ok { return entry } published := found.IsOnline entry := &MediaUsage{Type: UsageTypeArticle, ID: found.ID, Title: found.Title, Published: &published, Fields: []string{}} byID[found.ID] = entry return entry } func (u *GetMediaUsageUseCase) authorUsages(img image.Image) ([]MediaUsage, error) { found, err := u.authors.FindByAvatar(img.ID) if err != nil { return nil, err } usages := make([]MediaUsage, 0, len(found)) for _, a := range found { usages = append(usages, MediaUsage{Type: UsageTypeAuthor, ID: a.ID, Title: a.Name, Fields: []string{UsageFieldAvatar}}) } return usages, nil } func (u *GetMediaUsageUseCase) siteUsages(img image.Image) ([]MediaUsage, error) { all, err := u.sites.List() if err != nil { return nil, err } usages := []MediaUsage{} for _, s := range all { if s.DefaultSocialImageID != nil && *s.DefaultSocialImageID == img.ID { usages = append(usages, MediaUsage{Type: UsageTypeSite, ID: s.ID, Title: s.Name, Fields: []string{UsageFieldDefaultSocialImage}}) } } slices.SortFunc(usages, func(a, b MediaUsage) int { return int(a.ID) - int(b.ID) }) return usages, nil } func articleMentionsImage(a article.Article, img image.Image) bool { return strings.Contains(a.Body, image.StoredName(img.Path)) || a.UsesImage(img.ID) }