fix: use registered MIME type for uploaded files instead of image/<ext> - #1618
rstrzelecki-inne-projekty wants to merge 1 commit into
Conversation
The AvatarThumb middleware sets `content-type: image/<extension>` for every request under /uploads. For SVG files this yields `image/svg`, which browsers refuse to render inside <img> (they require `image/svg+xml`), so custom badge icons or branding files in SVG show up as broken images. Other extensions get nonsense types too (e.g. `image/txt`). Use mime.TypeByExtension and keep the previous guess only as a fallback for unknown extensions. Adds unit tests.
There was a problem hiding this comment.
Warning
Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.
Pull request overview
Updates the upload-serving middleware to set Content-Type using Go’s registered MIME types (fixing SVG rendering by using image/svg+xml), while preserving the legacy image/<ext> fallback for unknown extensions, and adds unit tests to cover the helper and middleware behavior.
Changes:
- Replace hardcoded
image/<ext>content type guessing withmime.TypeByExtension+ fallback. - Introduce
contentTypeByExthelper to centralize MIME resolution. - Add unit tests for MIME resolution and
/uploads/branding/*middleware behavior.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| internal/base/middleware/avatar.go | Uses mime.TypeByExtension via a new helper to set correct Content-Type for uploaded files (notably SVG). |
| internal/base/middleware/avatar_test.go | Adds tests verifying MIME mapping and middleware headers for SVG/PNG under /uploads/branding. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| func contentTypeByExt(ext string) string { | ||
| if ct := mime.TypeByExtension(strings.ToLower(ext)); ct != "" { | ||
| return ct | ||
| } | ||
| return fmt.Sprintf("image/%s", strings.TrimPrefix(ext, ".")) | ||
| } |
| func TestContentTypeByExt(t *testing.T) { | ||
| assert.Equal(t, "image/svg+xml", contentTypeByExt(".svg")) | ||
| assert.Equal(t, "image/png", contentTypeByExt(".png")) | ||
| assert.Equal(t, "image/jpeg", contentTypeByExt(".JPG")) | ||
| assert.Equal(t, "image/webp", contentTypeByExt(".webp")) | ||
| // unknown extension keeps the legacy behaviour | ||
| assert.Equal(t, "image/unknownext", contentTypeByExt(".unknownext")) | ||
| } |
| return | ||
| } | ||
| ctx.Header("content-type", fmt.Sprintf("image/%s", strings.TrimLeft(path.Ext(filePath), "."))) | ||
| ctx.Header("content-type", contentTypeByExt(path.Ext(filePath))) |
| } | ||
| ext := strings.TrimPrefix(filepath.Ext(urlInfo.Path), ".") | ||
| ctx.Header("content-type", fmt.Sprintf("image/%s", ext)) | ||
| ctx.Header("content-type", contentTypeByExt(filepath.Ext(urlInfo.Path))) |
Proposed Changes
AvatarThumbmiddleware setscontent-type: image/<extension>for every request under/uploads. For SVG files this producesimage/svg, which browsers refuse to render inside<img>(they requireimage/svg+xml), so SVG branding files or custom badge icons served from/uploads/branding/...show up as broken images. Other extensions get made-up types as well (e.g.image/txt).mime.TypeByExtension(Go's built-in table already knows.svg→image/svg+xml,.webp,.jpg, …) and keep the previousimage/<ext>guess only as a fallback for unknown extensions./uploads/branding/*.svgand*.png.How to reproduce: put
x.svginto/data/uploads/branding/and request it — response hasContent-Type: image/svg, and<img src="/uploads/branding/x.svg">does not render. With this change:image/svg+xml.