141 lines
3.9 KiB
Go
141 lines
3.9 KiB
Go
package handlers
|
||
|
||
import (
|
||
"bytes"
|
||
"context"
|
||
"fmt"
|
||
"image"
|
||
"image/jpeg"
|
||
"image/png"
|
||
"io"
|
||
"net/http"
|
||
oapi "nyanimedb/api"
|
||
"os"
|
||
"path/filepath"
|
||
"strings"
|
||
|
||
"github.com/disintegration/imaging"
|
||
log "github.com/sirupsen/logrus"
|
||
"golang.org/x/image/webp"
|
||
)
|
||
|
||
// PostMediaUpload implements oapi.StrictServerInterface.
|
||
func (s *Server) PostMediaUpload(ctx context.Context, request oapi.PostMediaUploadRequestObject) (oapi.PostMediaUploadResponseObject, error) {
|
||
// Получаем multipart body
|
||
mp := request.MultipartBody
|
||
if mp == nil {
|
||
log.Errorf("PostMedia without body")
|
||
return oapi.PostMediaUpload400JSONResponse("Multipart body is required"), nil
|
||
}
|
||
|
||
// Парсим первую часть (предполагаем, что файл в поле "file")
|
||
part, err := mp.NextPart()
|
||
if err != nil {
|
||
log.Errorf("PostMedia without file")
|
||
return oapi.PostMediaUpload400JSONResponse("File required"), nil
|
||
}
|
||
defer part.Close()
|
||
|
||
// Читаем ВЕСЬ файл в память (для небольших изображений — нормально)
|
||
// Если файлы могут быть большими — используйте лимитированный буфер (см. ниже)
|
||
data, err := io.ReadAll(part)
|
||
if err != nil {
|
||
log.Errorf("PostMedia cannot read file")
|
||
return oapi.PostMediaUpload400JSONResponse("File required"), nil
|
||
}
|
||
|
||
if len(data) == 0 {
|
||
log.Errorf("PostMedia empty file")
|
||
return oapi.PostMediaUpload400JSONResponse("Empty file"), nil
|
||
}
|
||
|
||
// Проверка MIME по первым 512 байтам
|
||
mimeType := http.DetectContentType(data)
|
||
if mimeType != "image/jpeg" && mimeType != "image/png" && mimeType != "image/webp" {
|
||
log.Errorf("PostMedia bad type")
|
||
return oapi.PostMediaUpload400JSONResponse("Bad data type"), nil
|
||
}
|
||
|
||
// Декодируем изображение из буфера
|
||
var img image.Image
|
||
switch mimeType {
|
||
case "image/jpeg":
|
||
{
|
||
img, err = jpeg.Decode(bytes.NewReader(data))
|
||
if err != nil {
|
||
log.Errorf("PostMedia cannot decode file: %v", err)
|
||
return oapi.PostMediaUpload500Response{}, nil
|
||
}
|
||
}
|
||
case "image/png":
|
||
{
|
||
img, err = png.Decode(bytes.NewReader(data))
|
||
if err != nil {
|
||
log.Errorf("PostMedia cannot decode file: %v", err)
|
||
return oapi.PostMediaUpload500Response{}, nil
|
||
}
|
||
}
|
||
case "image/webp":
|
||
{
|
||
img, err = webp.Decode(bytes.NewReader(data))
|
||
if err != nil {
|
||
log.Errorf("PostMedia cannot decode file: %v", err)
|
||
return oapi.PostMediaUpload500Response{}, nil
|
||
}
|
||
}
|
||
}
|
||
|
||
// Перекодируем в чистый JPEG (без EXIF, сжатие, RGB)
|
||
var buf bytes.Buffer
|
||
err = imaging.Encode(&buf, img, imaging.PNG)
|
||
if err != nil {
|
||
log.Errorf("PostMedia failed to re-encode JPEG: %v", err)
|
||
return oapi.PostMediaUpload500Response{}, nil
|
||
}
|
||
|
||
// TODO: to delete
|
||
filename := part.FileName()
|
||
if filename == "" {
|
||
filename = "upload_" + generateRandomHex(8) + ".jpg"
|
||
} else {
|
||
filename = sanitizeFilename(filename)
|
||
if !strings.HasSuffix(strings.ToLower(filename), ".jpg") {
|
||
filename += ".jpg"
|
||
}
|
||
}
|
||
|
||
// TODO: пойти на хуй ( вызвать файловую помойку)
|
||
err = os.WriteFile(filepath.Join("/uploads", filename), buf.Bytes(), 0644)
|
||
if err != nil {
|
||
log.Errorf("PostMedia failed to write: %v", err)
|
||
return oapi.PostMediaUpload500Response{}, nil
|
||
}
|
||
|
||
return oapi.PostMediaUpload200JSONResponse{}, nil
|
||
}
|
||
|
||
// Вспомогательные функции — как раньше
|
||
func generateRandomHex(n int) string {
|
||
b := make([]byte, n)
|
||
for i := range b {
|
||
b[i] = byte('a' + (i % 16))
|
||
}
|
||
return fmt.Sprintf("%x", b)
|
||
}
|
||
|
||
func sanitizeFilename(name string) string {
|
||
var clean strings.Builder
|
||
for _, r := range name {
|
||
if (r >= 'a' && r <= 'z') ||
|
||
(r >= 'A' && r <= 'Z') ||
|
||
(r >= '0' && r <= '9') ||
|
||
r == '.' || r == '_' || r == '-' {
|
||
clean.WriteRune(r)
|
||
}
|
||
}
|
||
s := clean.String()
|
||
if s == "" {
|
||
return "file"
|
||
}
|
||
return s
|
||
}
|