feat(API): media library
This commit is contained in:
1 parent
95ae043d3e
commit
0d99544579
65 files changed
+3992
-1530
No files matched your search
@@ -2,8 +2,26 @@ package useCases
|
||||
|
||||
import (
|
||||
"RenewCMS/internal/domain/image"
|
||||
"bytes"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"encoding/xml"
|
||||
stdimage "image"
|
||||
"io"
|
||||
|
||||
_ "golang.org/x/image/webp"
|
||||
_ "image/jpeg"
|
||||
_ "image/png"
|
||||
)
|
||||
|
||||
const svgRootElement = "svg"
|
||||
|
||||
var rasterFormats = map[string]string{
|
||||
"image/png": "png",
|
||||
"image/jpeg": "jpeg",
|
||||
"image/webp": "webp",
|
||||
}
|
||||
|
||||
type CreateImageUseCase struct {
|
||||
imageRepository image.Repository
|
||||
}
|
||||
@@ -16,8 +34,68 @@ func (g *CreateImageUseCase) CreateImage(input image.ImageInput) (image.Image, e
|
||||
if !image.IsAllowedContentType(input.ContentType) {
|
||||
return image.Image{}, image.ErrImageTypeNotAllowed
|
||||
}
|
||||
if input.Size > image.MaxImageSize {
|
||||
return image.Image{}, image.ErrImageTooLarge
|
||||
|
||||
data, err := readWithinLimit(input.Content)
|
||||
if err != nil {
|
||||
return image.Image{}, err
|
||||
}
|
||||
|
||||
width, height, err := measure(data, input.ContentType)
|
||||
if err != nil {
|
||||
return image.Image{}, err
|
||||
}
|
||||
|
||||
checksum := sha256.Sum256(data)
|
||||
return g.imageRepository.Create(image.ImageInput{
|
||||
Content: bytes.NewReader(data),
|
||||
Filename: image.NormalizeUploadFilename(input.Filename, input.ContentType),
|
||||
Size: int64(len(data)),
|
||||
ContentType: input.ContentType,
|
||||
Width: width,
|
||||
Height: height,
|
||||
SHA256: hex.EncodeToString(checksum[:]),
|
||||
UploadedByID: input.UploadedByID,
|
||||
})
|
||||
}
|
||||
|
||||
func readWithinLimit(content io.Reader) ([]byte, error) {
|
||||
if content == nil {
|
||||
return nil, image.ErrImageUnreadable
|
||||
}
|
||||
data, err := io.ReadAll(io.LimitReader(content, image.MaxImageSize+1))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if int64(len(data)) > image.MaxImageSize {
|
||||
return nil, image.ErrImageTooLarge
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func measure(data []byte, contentType string) (int, int, error) {
|
||||
if contentType == "image/svg+xml" {
|
||||
if !hasSvgRoot(data) {
|
||||
return 0, 0, image.ErrImageUnreadable
|
||||
}
|
||||
return 0, 0, nil
|
||||
}
|
||||
|
||||
config, format, err := stdimage.DecodeConfig(bytes.NewReader(data))
|
||||
if err != nil || format != rasterFormats[contentType] {
|
||||
return 0, 0, image.ErrImageUnreadable
|
||||
}
|
||||
return config.Width, config.Height, nil
|
||||
}
|
||||
|
||||
func hasSvgRoot(data []byte) bool {
|
||||
decoder := xml.NewDecoder(bytes.NewReader(data))
|
||||
for {
|
||||
token, err := decoder.Token()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
if start, ok := token.(xml.StartElement); ok {
|
||||
return start.Name.Local == svgRootElement
|
||||
}
|
||||
}
|
||||
return g.imageRepository.Create(input)
|
||||
}
|
||||
Reference in new issue
Block a user