From f43b06e13d3fe88e2b224e2943e783cc459c1bf3 Mon Sep 17 00:00:00 2001 From: zerosaturation Date: Tue, 7 Apr 2026 23:59:00 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=20minimax=5Fservice?= =?UTF-8?q?=20=E5=AE=89=E5=85=A8=E9=9A=90=E6=82=A3=E5=92=8C=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E7=AB=9E=E4=BA=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加 HTTP 响应状态码检查,防止状态错误被忽略 - 添加 HTTP Client 结构体复用,避免每次创建新客户端 - 在 compressImageIfNeeded 中使用带 Timeout 的 Client - 添加 URL scheme 校验,防止 SSRF 攻击 - 使用 LimitedReader 限制下载图片大小(10MB) - 修复 GetJob 数据竞争,复制数据后返回 Co-Authored-By: Claude Opus 4.6 --- .../assetService/service/minimax_service.go | 24 +++++++++++++++---- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/backend/services/assetService/service/minimax_service.go b/backend/services/assetService/service/minimax_service.go index 2a14bf3..6906827 100644 --- a/backend/services/assetService/service/minimax_service.go +++ b/backend/services/assetService/service/minimax_service.go @@ -61,6 +61,7 @@ type minimaxService struct { config *config.AssetConfig jobs map[string]*ImageGenerationJob jobsLock sync.RWMutex + client *http.Client } // NewMinimaxService 创建 MiniMax 服务 @@ -68,6 +69,7 @@ func NewMinimaxService(cfg *config.AssetConfig) MinimaxService { svc := &minimaxService{ config: cfg, jobs: make(map[string]*ImageGenerationJob), + client: &http.Client{Timeout: 120 * time.Second}, } go svc.cleanupExpiredJobs() return svc @@ -111,7 +113,11 @@ func (s *minimaxService) GetJob(ctx context.Context, jobID string, userID, starI return nil, fmt.Errorf("access denied") } - return job, nil + // Copy data to avoid race + result := *job + result.Images = make([]string, len(job.Images)) + copy(result.Images, job.Images) + return &result, nil } // processJob 异步处理任务 @@ -192,7 +198,6 @@ func (s *minimaxService) callMiniMaxAPI(model, prompt, aspectRatio string, refs return nil, err } - client := &http.Client{Timeout: 120 * time.Second} req, err := http.NewRequest("POST", apiURL, bytes.NewBuffer(jsonData)) if err != nil { return nil, err @@ -200,12 +205,17 @@ func (s *minimaxService) callMiniMaxAPI(model, prompt, aspectRatio string, refs req.Header.Set("Authorization", "Bearer "+apiKey) req.Header.Set("Content-Type", "application/json") - resp, err := client.Do(req) + resp, err := s.client.Do(req) if err != nil { return nil, err } defer resp.Body.Close() + if resp.StatusCode < 200 || resp.StatusCode >= 300 { + body, _ := io.ReadAll(resp.Body) + return nil, fmt.Errorf("API returned status %d: %s", resp.StatusCode, string(body)) + } + var result struct { Images []struct { URL string `json:"url"` @@ -224,13 +234,14 @@ func (s *minimaxService) callMiniMaxAPI(model, prompt, aspectRatio string, refs // compressImageIfNeeded 下载并压缩图片 func (s *minimaxService) compressImageIfNeeded(imageURL string) (string, error) { - resp, err := http.Get(imageURL) + client := &http.Client{Timeout: 30 * time.Second} + resp, err := client.Get(imageURL) if err != nil { return "", err } defer resp.Body.Close() - imgData, err := io.ReadAll(resp.Body) + imgData, err := io.ReadAll(io.LimitReader(resp.Body, 10*1024*1024)) // 10MB limit if err != nil { return "", err } @@ -295,6 +306,9 @@ func validateURL(rawURL string) error { if err != nil { return err } + if u.Scheme != "http" && u.Scheme != "https" { + return fmt.Errorf("unsupported scheme: %s", u.Scheme) + } host := u.Hostname() ip := net.ParseIP(host)