diff --git a/internal/imgcache/service.go b/internal/imgcache/service.go index a53825f..849a90d 100644 --- a/internal/imgcache/service.go +++ b/internal/imgcache/service.go @@ -13,6 +13,7 @@ import ( "github.com/dustin/go-humanize" "sneak.berlin/go/pixa/internal/allowlist" "sneak.berlin/go/pixa/internal/imageprocessor" + "sneak.berlin/go/pixa/internal/magic" ) // Service implements the ImageCache interface, orchestrating cache, fetcher, and processor. @@ -277,7 +278,7 @@ func (s *Service) fetchAndProcess( ) // Validate magic bytes match content type - if err := ValidateMagicBytes(sourceData, fetchResult.ContentType); err != nil { + if err := magic.ValidateMagicBytes(sourceData, fetchResult.ContentType); err != nil { return nil, fmt.Errorf("content validation failed: %w", err) } diff --git a/internal/imgcache/service_test.go b/internal/imgcache/service_test.go index f99008e..5e0b7be 100644 --- a/internal/imgcache/service_test.go +++ b/internal/imgcache/service_test.go @@ -5,6 +5,8 @@ import ( "io" "testing" "time" + + "sneak.berlin/go/pixa/internal/magic" ) func TestService_Get_WhitelistedHost(t *testing.T) { @@ -315,17 +317,17 @@ func TestService_Get_FormatConversion(t *testing.T) { t.Fatalf("failed to read response: %v", err) } - detectedMIME, err := DetectFormat(data) + detectedMIME, err := magic.DetectFormat(data) if err != nil { t.Fatalf("failed to detect format: %v", err) } - expectedFormat, ok := MIMEToImageFormat(tt.wantMIME) + expectedFormat, ok := magic.MIMEToImageFormat(tt.wantMIME) if !ok { t.Fatalf("unknown format for MIME type: %s", tt.wantMIME) } - detectedFormat, ok := MIMEToImageFormat(string(detectedMIME)) + detectedFormat, ok := magic.MIMEToImageFormat(string(detectedMIME)) if !ok { t.Fatalf("unknown format for detected MIME type: %s", detectedMIME) } diff --git a/internal/imgcache/magic.go b/internal/magic/magic.go similarity index 90% rename from internal/imgcache/magic.go rename to internal/magic/magic.go index 3643cf8..40eeaee 100644 --- a/internal/imgcache/magic.go +++ b/internal/magic/magic.go @@ -1,4 +1,6 @@ -package imgcache +// Package magic detects image formats from magic bytes and validates +// content against declared MIME types. +package magic import ( "bytes" @@ -27,6 +29,20 @@ const ( MIMETypeSVG = MIMEType("image/svg+xml") ) +// ImageFormat represents supported output image formats. +// This mirrors the type in imgcache to avoid circular imports. +type ImageFormat string + +// Supported image output formats. +const ( + FormatOriginal ImageFormat = "orig" + FormatJPEG ImageFormat = "jpeg" + FormatPNG ImageFormat = "png" + FormatWebP ImageFormat = "webp" + FormatAVIF ImageFormat = "avif" + FormatGIF ImageFormat = "gif" +) + // MinMagicBytes is the minimum number of bytes needed to detect format. const MinMagicBytes = 12 @@ -189,7 +205,7 @@ func PeekAndValidate(r io.Reader, declaredType string) (io.Reader, error) { return io.MultiReader(bytes.NewReader(buf), r), nil } -// MIMEToImageFormat converts a MIME type to our ImageFormat type. +// MIMEToImageFormat converts a MIME type to an ImageFormat. func MIMEToImageFormat(mimeType string) (ImageFormat, bool) { normalized := normalizeMIMEType(mimeType) switch MIMEType(normalized) { @@ -208,7 +224,7 @@ func MIMEToImageFormat(mimeType string) (ImageFormat, bool) { } } -// ImageFormatToMIME converts our ImageFormat to a MIME type string. +// ImageFormatToMIME converts an ImageFormat to a MIME type string. func ImageFormatToMIME(format ImageFormat) string { switch format { case FormatJPEG: diff --git a/internal/imgcache/magic_test.go b/internal/magic/magic_test.go similarity index 99% rename from internal/imgcache/magic_test.go rename to internal/magic/magic_test.go index 36d8bd5..9266111 100644 --- a/internal/imgcache/magic_test.go +++ b/internal/magic/magic_test.go @@ -1,4 +1,4 @@ -package imgcache +package magic import ( "bytes"