Compare commits

..

1 Commits

Author SHA1 Message Date
58aa72f9bb fix(importer): gracefully skip files with no download url 2025-06-19 18:10:43 +03:00

View File

@@ -22,8 +22,8 @@ import (
type CurseForgeImporter struct { type CurseForgeImporter struct {
StoragePath string StoragePath string
APIKey string APIKey string
HTTPClient *http.Client // Для быстрых API-запросов HTTPClient *http.Client
HTTPClientLong *http.Client // ИСПРАВЛЕНИЕ: Добавляем это поле в структуру HTTPClientLong *http.Client
} }
// NewCurseForgeImporter создает новый экземпляр импортера. // NewCurseForgeImporter создает новый экземпляр импортера.
@@ -38,7 +38,6 @@ func NewCurseForgeImporter(storagePath, apiKey string) *CurseForgeImporter {
// downloadAndProcessFile скачивает файл по URL и передает его в общий обработчик. // downloadAndProcessFile скачивает файл по URL и передает его в общий обработчик.
func (i *CurseForgeImporter) downloadAndProcessFile(url string) (hash string, size int64, err error) { func (i *CurseForgeImporter) downloadAndProcessFile(url string) (hash string, size int64, err error) {
// Используем клиент с длинным таймаутом и для скачивания отдельных модов
req, err := http.NewRequestWithContext(context.Background(), "GET", url, nil) req, err := http.NewRequestWithContext(context.Background(), "GET", url, nil)
if err != nil { if err != nil {
return "", 0, err return "", 0, err
@@ -67,7 +66,6 @@ func (i *CurseForgeImporter) getFileInfo(projectID, fileID int) (*CurseForgeFile
} }
req.Header.Set("x-api-key", i.APIKey) req.Header.Set("x-api-key", i.APIKey)
// Используем клиент с коротким таймаутом для быстрых API-запросов
resp, err := i.HTTPClient.Do(req) resp, err := i.HTTPClient.Do(req)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -83,10 +81,6 @@ func (i *CurseForgeImporter) getFileInfo(projectID, fileID int) (*CurseForgeFile
return nil, fmt.Errorf("failed to decode file info response: %w", err) return nil, fmt.Errorf("failed to decode file info response: %w", err)
} }
if fileInfo.Data.DownloadURL == "" {
return nil, fmt.Errorf("received empty download URL from API for fileID %d", fileID)
}
return &fileInfo, nil return &fileInfo, nil
} }
@@ -227,15 +221,24 @@ func (i *CurseForgeImporter) Import(zipPath string) ([]models.ModpackFile, error
for _, modFile := range manifest.Files { for _, modFile := range manifest.Files {
fileInfo, err := i.getFileInfo(modFile.ProjectID, modFile.FileID) fileInfo, err := i.getFileInfo(modFile.ProjectID, modFile.FileID)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to get info for fileID %d: %w", modFile.FileID, err) log.Printf("Importer: WARN - Could not get info for fileID %d, skipping. Error: %v", modFile.FileID, err)
continue
} }
downloadURL := fileInfo.Data.DownloadURL downloadURL := fileInfo.Data.DownloadURL
fileName := fileInfo.Data.FileName fileName := fileInfo.Data.FileName
// ИСПРАВЛЕНИЕ: Проверяем, что URL не пустой
if downloadURL == "" {
log.Printf("Importer: WARN - Empty download URL for file '%s' (fileID %d), skipping.", fileName, modFile.FileID)
continue // Пропускаем этот файл
}
hash, size, err := i.downloadAndProcessFile(downloadURL) hash, size, err := i.downloadAndProcessFile(downloadURL)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to process downloaded file '%s' (fileID %d): %w", fileName, modFile.FileID, err) // Вместо того чтобы падать, просто логируем ошибку и пропускаем файл
log.Printf("Importer: WARN - Failed to process downloaded file '%s' (fileID %d), skipping. Error: %v", fileName, modFile.FileID, err)
continue
} }
relativePath := filepath.Join("mods", fileName) relativePath := filepath.Join("mods", fileName)