package persistence import ( domain "RenewCMS/internal/domain/image" "RenewCMS/internal/infrastructure/persistence/mappers" entity "RenewCMS/internal/infrastructure/persistence/models" "strings" "gorm.io/gorm" ) type ImageRepository struct { db *gorm.DB } func NewImageRepository(db *gorm.DB) *ImageRepository { return &ImageRepository{db} } func (i ImageRepository) Create(image domain.Image) (domain.Image, error) { model := mappers.ImageToModel(image) if err := i.db.Create(&model).Error; err != nil { return domain.Image{}, err } return mappers.ImageToDomain(model), nil } func (i ImageRepository) Get(id uint32) (domain.Image, error) { var model entity.Image err := i.db.First(&model, id).Error if err != nil { return domain.Image{}, mapNotFoundErr(err, domain.ErrImageNotFound) } return mappers.ImageToDomain(model), nil } func (i ImageRepository) Delete(id uint32) error { return deleteOrNotFound(i.db.Delete(&entity.Image{}, id), domain.ErrImageNotFound) } func (i ImageRepository) List(query domain.SearchQuery) ([]domain.Image, error) { statement := i.db.Model(&entity.Image{}) if query.Search != "" { statement = statement.Where("LOWER(filename) LIKE ? ESCAPE '!'", containsPattern(strings.ToLower(query.Search))) } if query.Hash != "" { statement = statement.Where("sha256 = ?", query.Hash) } var models []entity.Image if err := statement.Order("id asc").Find(&models).Error; err != nil { return nil, err } return imagesToDomain(models), nil } func (i ImageRepository) FindByHash(hash string) ([]domain.Image, error) { if hash == "" { return []domain.Image{}, nil } var models []entity.Image if err := i.db.Where("sha256 = ?", hash).Order("id asc").Find(&models).Error; err != nil { return nil, err } return imagesToDomain(models), nil } func (i ImageRepository) Rename(id uint32, filename string) (domain.Image, error) { var model entity.Image err := i.db.First(&model, id).Error if err != nil { return domain.Image{}, mapNotFoundErr(err, domain.ErrImageNotFound) } model.Filename = filename if err := i.db.Save(&model).Error; err != nil { return domain.Image{}, err } return mappers.ImageToDomain(model), nil } func (i ImageRepository) ListDuplicates() ([]domain.Image, error) { var models []entity.Image err := i.db.Where("sha256 IN (?)", i.duplicatedHashes()).Order("sha256 asc, id asc").Find(&models).Error if err != nil { return nil, err } return imagesToDomain(models), nil } func (i ImageRepository) Counts() (domain.Counts, error) { var all int64 if err := i.db.Model(&entity.Image{}).Count(&all).Error; err != nil { return domain.Counts{}, err } var groups int64 if err := i.db.Table("(?) as duplicated", i.duplicatedHashes()).Count(&groups).Error; err != nil { return domain.Counts{}, err } return domain.Counts{All: int(all), DuplicateGroups: int(groups)}, nil } func (i ImageRepository) ExistsByStoredName(storedName string) (bool, error) { var matches int64 err := i.db.Model(&entity.Image{}).Where("path = ?", domain.PublicPath(storedName)).Limit(1).Count(&matches).Error return matches > 0, err } func (i ImageRepository) duplicatedHashes() *gorm.DB { return i.db.Model(&entity.Image{}). Select("sha256"). Where("sha256 <> ''"). Group("sha256"). Having("COUNT(*) > 1") } func imagesToDomain(models []entity.Image) []domain.Image { images := make([]domain.Image, 0, len(models)) for _, model := range models { images = append(images, mappers.ImageToDomain(model)) } return images } func (i ImageRepository) ListMissingMetadata() ([]domain.Image, error) { var models []entity.Image err := i.db.Where("filename = '' OR filename IS NULL").Order("id asc").Find(&models).Error if err != nil { return nil, err } return imagesToDomain(models), nil } func (i ImageRepository) UpdateMetadata(id uint32, metadata domain.Metadata) error { return i.db.Model(&entity.Image{}).Where("id = ?", id).UpdateColumns(map[string]any{ "filename": metadata.Filename, "size": metadata.Size, "content_type": metadata.ContentType, "width": metadata.Width, "height": metadata.Height, "sha256": metadata.SHA256, }).Error } var _ domain.Repository = &ImageRepository{}