From 3b74bf16adbdf70fe7bfc01fee417a1f08f31787 Mon Sep 17 00:00:00 2001 From: Vladimir Zagainov Date: Thu, 19 Jun 2025 17:46:01 +0300 Subject: [PATCH] fix(importer): use full file info endpoint for curseforge --- internal/core/importer/curseforge.go | 37 +++++++++++----------- internal/core/importer/curseforge_types.go | 10 ++++++ 2 files changed, 29 insertions(+), 18 deletions(-) diff --git a/internal/core/importer/curseforge.go b/internal/core/importer/curseforge.go index 9e38348..3ae6f84 100644 --- a/internal/core/importer/curseforge.go +++ b/internal/core/importer/curseforge.go @@ -55,37 +55,35 @@ func (i *CurseForgeImporter) downloadAndProcessFile(url string) (hash string, si return baseImporter.processFile(resp.Body) } -// getFileDownloadURL получает прямую ссылку на скачивание файла с API CurseForge. -func (i *CurseForgeImporter) getFileDownloadURL(projectID, fileID int) (string, error) { - apiURL := fmt.Sprintf("https://api.curseforge.com/v1/mods/%d/files/%d/download-url", projectID, fileID) +// getFileInfo получает полную информацию о файле, включая URL для скачивания. +func (i *CurseForgeImporter) getFileInfo(projectID, fileID int) (*CurseForgeFile, error) { + apiURL := fmt.Sprintf("https://api.curseforge.com/v1/mods/%d/files/%d", projectID, fileID) req, err := http.NewRequestWithContext(context.Background(), "GET", apiURL, nil) if err != nil { - return "", err + return nil, err } req.Header.Set("x-api-key", i.APIKey) resp, err := i.HTTPClient.Do(req) if err != nil { - return "", err + return nil, err } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { - return "", fmt.Errorf("bad status getting download URL: %s", resp.Status) + return nil, fmt.Errorf("bad status getting file info for fileID %d: %s", fileID, resp.Status) } - var result struct { - Data string `json:"data"` - } - if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { - return "", fmt.Errorf("failed to decode download URL response: %w", err) + var fileInfo CurseForgeFile + if err := json.NewDecoder(resp.Body).Decode(&fileInfo); err != nil { + return nil, fmt.Errorf("failed to decode file info response: %w", err) } - if result.Data == "" { - return "", fmt.Errorf("received empty download URL from API") + if fileInfo.Data.DownloadURL == "" { + return nil, fmt.Errorf("received empty download URL from API for fileID %d", fileID) } - return result.Data, nil + return &fileInfo, nil } // findModpackBySlug ищет ID проекта по его "слагу" (части URL). @@ -223,17 +221,20 @@ func (i *CurseForgeImporter) Import(zipPath string) ([]models.ModpackFile, error var files []models.ModpackFile for _, modFile := range manifest.Files { - downloadURL, err := i.getFileDownloadURL(modFile.ProjectID, modFile.FileID) + fileInfo, err := i.getFileInfo(modFile.ProjectID, modFile.FileID) if err != nil { - return nil, fmt.Errorf("failed to get download url for fileID %d: %w", modFile.FileID, err) + return nil, fmt.Errorf("failed to get info for fileID %d: %w", modFile.FileID, err) } + downloadURL := fileInfo.Data.DownloadURL + fileName := fileInfo.Data.FileName + hash, size, err := i.downloadAndProcessFile(downloadURL) if err != nil { - return nil, fmt.Errorf("failed to process downloaded file for fileID %d: %w", modFile.FileID, err) + return nil, fmt.Errorf("failed to process downloaded file '%s' (fileID %d): %w", fileName, modFile.FileID, err) } - relativePath := filepath.Join("mods", fmt.Sprintf("%d-%d.jar", modFile.ProjectID, modFile.FileID)) + relativePath := filepath.Join("mods", fileName) files = append(files, models.ModpackFile{ RelativePath: relativePath, diff --git a/internal/core/importer/curseforge_types.go b/internal/core/importer/curseforge_types.go index 2fc115a..207ee16 100644 --- a/internal/core/importer/curseforge_types.go +++ b/internal/core/importer/curseforge_types.go @@ -40,3 +40,13 @@ type CurseForgeFilesResponse struct { TotalCount int `json:"totalCount"` } `json:"pagination"` } + +// CurseForgeFile представляет полную информацию о файле с API. +type CurseForgeFile struct { + Data struct { + ID int `json:"id"` + FileName string `json:"fileName"` + DownloadURL string `json:"downloadUrl"` + // ... можно добавить другие поля, если понадобятся + } `json:"data"` +}