From c58b578023d709fd794252eea8029e74881b69ed Mon Sep 17 00:00:00 2001 From: Iron_Felix Date: Sat, 20 Dec 2025 00:17:54 +0300 Subject: [PATCH] fix --- modules/backend/handlers/common.go | 14 ++-- modules/backend/handlers/images.go | 109 ++++++++--------------------- modules/backend/main.go | 2 +- modules/backend/types.go | 16 ++--- 4 files changed, 47 insertions(+), 94 deletions(-) diff --git a/modules/backend/handlers/common.go b/modules/backend/handlers/common.go index fc045ed..b75fb66 100644 --- a/modules/backend/handlers/common.go +++ b/modules/backend/handlers/common.go @@ -18,16 +18,16 @@ import ( // } type Server struct { - db *sqlc.Queries - ImageServerSocket string - RPCclient *rmq.RPCClient + db *sqlc.Queries + ImageServerURL string + RPCclient *rmq.RPCClient } -func NewServer(db *sqlc.Queries, ImageServerSocket string, rpcclient *rmq.RPCClient) *Server { +func NewServer(db *sqlc.Queries, ImageServerURL string, rpcclient *rmq.RPCClient) *Server { return &Server{ - db: db, - ImageServerSocket: ImageServerSocket, - RPCclient: rpcclient, + db: db, + ImageServerURL: ImageServerURL, + RPCclient: rpcclient, } } diff --git a/modules/backend/handlers/images.go b/modules/backend/handlers/images.go index dde4f99..5c8dce7 100644 --- a/modules/backend/handlers/images.go +++ b/modules/backend/handlers/images.go @@ -10,7 +10,6 @@ import ( "image/png" "io" "mime/multipart" - "net" "net/http" oapi "nyanimedb/api" sqlc "nyanimedb/sql" @@ -24,14 +23,13 @@ import ( // PostMediaUpload implements oapi.StrictServerInterface. func (s *Server) PostMediaUpload(ctx context.Context, request oapi.PostMediaUploadRequestObject) (oapi.PostMediaUploadResponseObject, error) { - // Получаем multipart body + // 1. Получаем 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") @@ -39,7 +37,6 @@ func (s *Server) PostMediaUpload(ctx context.Context, request oapi.PostMediaUplo } defer part.Close() - // Читаем ВЕСЬ файл в память data, err := io.ReadAll(part) if err != nil { log.Errorf("PostMedia cannot read file") @@ -47,108 +44,65 @@ func (s *Server) PostMediaUpload(ctx context.Context, request oapi.PostMediaUplo } if len(data) == 0 { - log.Errorf("PostMedia empty file") return oapi.PostMediaUpload400JSONResponse("Empty file"), nil } - // Проверка MIME по первым 512 байтам + // 2. Проверка и декодирование (оставляем как было) 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 - } - } + img, err = jpeg.Decode(bytes.NewReader(data)) 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 - } - } + img, err = png.Decode(bytes.NewReader(data)) 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 - } - } + img, err = webp.Decode(bytes.NewReader(data)) + default: + log.Errorf("PostMedia unsupported type: %s", mimeType) + return oapi.PostMediaUpload400JSONResponse("Unsupported image type"), nil } - // Перекодируем в PNG (как было в оригинале) - var buf bytes.Buffer - err = imaging.Encode(&buf, img, imaging.PNG) if err != nil { - log.Errorf("PostMedia failed to re-encode JPEG: %v", err) + log.Errorf("PostMedia decode error: %v", err) return oapi.PostMediaUpload500Response{}, nil } - // --------------------------------------------------------- - // Взаимодействие с Image Storage Service через Unix Socket - // --------------------------------------------------------- + // 3. Перекодируем в PNG перед отправкой + var buf bytes.Buffer + if err := imaging.Encode(&buf, img, imaging.PNG); err != nil { + log.Errorf("PostMedia encode error: %v", err) + return oapi.PostMediaUpload500Response{}, nil + } - // 1. Формируем Multipart Body для запроса к хранилищу + // 4. Подготавливаем запрос к внешнему хранилищу (Image Server) body := &bytes.Buffer{} writer := multipart.NewWriter(body) - // Поле "file" (обязательное по спецификации хранилища) - // Имя файла ставим фиксированное, так как хранилище генерирует sha1 - partWriter, err := writer.CreateFormFile("file", "upload.png") + // Создаем часть с файлом + partWriter, err := writer.CreateFormFile("file", "image.png") if err != nil { - log.Errorf("PostMedia failed to create form file: %v", err) return oapi.PostMediaUpload500Response{}, nil } - - // Копируем перекодированное PNG изображение в форму if _, err := io.Copy(partWriter, &buf); err != nil { - log.Errorf("PostMedia failed to write body: %v", err) return oapi.PostMediaUpload500Response{}, nil } - // Поле "subdir" (опционально, ставим "posters" или можно вынести в конфиг) - if err := writer.WriteField("subdir", "posters"); err != nil { - log.Errorf("PostMedia failed to write field subdir: %v", err) - return oapi.PostMediaUpload500Response{}, nil - } + // Добавляем subdir + _ = writer.WriteField("subdir", "posters") + writer.Close() - // Закрываем writer, чтобы записать boundary - if err := writer.Close(); err != nil { - log.Errorf("PostMedia failed to close multipart writer: %v", err) - return oapi.PostMediaUpload500Response{}, nil - } + // Формируем полный URL (s.AppConfig.ImageSocket теперь base_url, например "http://storage.local") + // Убедимся, что путь не дублирует слэши + uploadURL := strings.TrimSuffix(s.ImageServerURL, "/") + "/upload" - // 2. Настраиваем клиент для Unix сокета - socketPath := s.ImageServerSocket - transport := &http.Transport{ - DialContext: func(_ context.Context, _, _ string) (net.Conn, error) { - return net.Dial("unix", socketPath) - }, - } - client := &http.Client{Transport: transport} - - // 3. Создаем запрос - // Хост в URL ("http://unix") игнорируется транспортом, важен путь "/upload" - reqUpstream, err := http.NewRequest("POST", "http://unix/upload", body) + // 5. Отправляем обычный HTTP POST + reqUpstream, err := http.NewRequestWithContext(ctx, "POST", uploadURL, body) if err != nil { - log.Errorf("PostMedia failed to create upstream request: %v", err) return oapi.PostMediaUpload500Response{}, nil } reqUpstream.Header.Set("Content-Type", writer.FormDataContentType()) - // 4. Отправляем запрос - resp, err := client.Do(reqUpstream) + resp, err := http.DefaultClient.Do(reqUpstream) if err != nil { log.Errorf("PostMedia upstream request failed: %v", err) return oapi.PostMediaUpload500Response{}, nil @@ -156,21 +110,20 @@ func (s *Server) PostMediaUpload(ctx context.Context, request oapi.PostMediaUplo defer resp.Body.Close() if resp.StatusCode != http.StatusOK { - bodyErr, _ := io.ReadAll(resp.Body) - log.Errorf("PostMedia upstream error %d: %s", resp.StatusCode, string(bodyErr)) + log.Errorf("PostMedia storage returned status: %d", resp.StatusCode) return oapi.PostMediaUpload500Response{}, nil } - // 5. Разбираем ответ {"path": "..."} + // 6. Получаем путь из ответа хранилища var storageResp struct { Path string `json:"path"` } if err := json.NewDecoder(resp.Body).Decode(&storageResp); err != nil { - log.Errorf("PostMedia failed to decode upstream response: %v", err) return oapi.PostMediaUpload500Response{}, nil } - log.Infof("File uploaded to image storage: %s", storageResp.Path) + // В storageResp.Path теперь лежит что-то вроде "posters/a1/b2/hash.png" + log.Infof("Successfully uploaded: %s", storageResp.Path) params := sqlc.CreateImageParams{ StorageType: sqlc.StorageTypeTLocal, diff --git a/modules/backend/main.go b/modules/backend/main.go index f95dee6..a67e533 100644 --- a/modules/backend/main.go +++ b/modules/backend/main.go @@ -62,7 +62,7 @@ func main() { rpcClient := rmq.NewRPCClient(rmqConn, 30*time.Second) - server := handlers.NewServer(queries, AppConfig.ImageServerSocket, rpcClient) + server := handlers.NewServer(queries, AppConfig.ImageServerURL, rpcClient) r.Use(cors.New(cors.Config{ AllowOrigins: []string{AppConfig.ServiceAddress}, diff --git a/modules/backend/types.go b/modules/backend/types.go index 0429187..36c5380 100644 --- a/modules/backend/types.go +++ b/modules/backend/types.go @@ -1,12 +1,12 @@ package main type Config struct { - Mode string - ServiceAddress string `toml:"ServiceAddress" env:"SERVICE_ADDRESS"` - DdUrl string `toml:"DbUrl" env:"DATABASE_URL"` - JwtPrivateKey string `toml:"JwtPrivateKey" env:"JWT_PRIVATE_KEY"` - LogLevel string `toml:"LogLevel" env:"LOG_LEVEL"` - RmqURL string `toml:"RabbitMQUrl" env:"RABBITMQ_URL"` - AuthEnabled string `toml:"AuthEnabled" env:"AUTH_ENABLED"` - ImageServerSocket string `toml:"ImageServerSocket" env:"IMAGES_BASE_URL"` + Mode string + ServiceAddress string `toml:"ServiceAddress" env:"SERVICE_ADDRESS"` + DdUrl string `toml:"DbUrl" env:"DATABASE_URL"` + JwtPrivateKey string `toml:"JwtPrivateKey" env:"JWT_PRIVATE_KEY"` + LogLevel string `toml:"LogLevel" env:"LOG_LEVEL"` + RmqURL string `toml:"RabbitMQUrl" env:"RABBITMQ_URL"` + AuthEnabled string `toml:"AuthEnabled" env:"AUTH_ENABLED"` + ImageServerURL string `toml:"ImageServerURL" env:"IMAGES_BASE_URL"` }