package useCases import ( "RenewCMS/internal/domain/image" "bytes" "crypto/sha256" "encoding/hex" stdimage "image" "io" "path" _ "golang.org/x/image/webp" _ "image/jpeg" _ "image/png" ) var extensionContentTypes = map[string]string{ ".png": "image/png", ".jpg": "image/jpeg", ".jpeg": "image/jpeg", ".webp": "image/webp", ".svg": "image/svg+xml", } type BackfillImageMetadataUseCase struct { imageRepository image.Repository storage image.Storage } func NewBackfillImageMetadataUseCase(imageRepository image.Repository, storage image.Storage) *BackfillImageMetadataUseCase { return &BackfillImageMetadataUseCase{imageRepository, storage} } func (u *BackfillImageMetadataUseCase) Run() error { pending, err := u.imageRepository.ListMissingMetadata() if err != nil { return err } for _, row := range pending { metadata, ok := u.readStoredImage(row.Path) if !ok { continue } if err := u.imageRepository.UpdateMetadata(row.ID, metadata); err != nil { return err } } return nil } func (u *BackfillImageMetadataUseCase) readStoredImage(storedPath string) (image.Metadata, bool) { fileName := image.StoredName(storedPath) contentType, known := extensionContentTypes[path.Ext(fileName)] if !known { return image.Metadata{}, false } data, err := u.read(fileName) if err != nil { return image.Metadata{}, false } width, height := 0, 0 if contentType != "image/svg+xml" { config, _, err := stdimage.DecodeConfig(bytes.NewReader(data)) if err != nil { return image.Metadata{}, false } width, height = config.Width, config.Height } checksum := sha256.Sum256(data) return image.Metadata{ Filename: fileName, Size: int64(len(data)), ContentType: contentType, Width: width, Height: height, SHA256: hex.EncodeToString(checksum[:]), }, true } func (u *BackfillImageMetadataUseCase) read(storedName string) ([]byte, error) { blob, err := u.storage.Open(storedName) if err != nil { return nil, err } defer blob.Content.Close() return io.ReadAll(blob.Content) }