package services import ( "encoding/json" "fmt" "io" "net/http" "os" "path/filepath" "strings" "time" "fotbal-club/internal/config" "fotbal-club/internal/models" "gorm.io/gorm" ) type logoAPIResponse struct { LogoURLSVG string `json:"logo_url_svg"` LogoURLPNG string `json:"logo_url_png"` LogoURL string `json:"logo_url"` } func CacheClubLogo(db *gorm.DB, clubID string) (string, error) { cid := strings.TrimSpace(clubID) if cid == "" { return "", fmt.Errorf("empty club id") } baseUpload := config.AppConfig.UploadDir if strings.TrimSpace(baseUpload) == "" { baseUpload = "./uploads" } destDir := filepath.Join(baseUpload, "logos", "club", cid) _ = os.MkdirAll(destDir, 0o755) checkExisting := func() (string, bool) { exts := []string{".svg", ".png", ".jpg", ".jpeg", ".webp"} for _, ext := range exts { p := filepath.Join(destDir, "club-logo"+ext) if fi, err := os.Stat(p); err == nil && fi.Size() > 0 { pub := "/uploads/" + filepath.ToSlash(filepath.Join("logos", "club", cid, "club-logo"+ext)) return pub, true } } return "", false } if url, ok := checkExisting(); ok { return url, nil } client := &http.Client{Timeout: 12 * time.Second} req, err := http.NewRequest("GET", "https://logoapi.sportcreative.eu/logos/"+cid+"/json", nil) if err != nil { return "", err } req.Header.Set("Accept", "application/json") req.Header.Set("User-Agent", "fotbal-club/logo-cache") resp, err := client.Do(req) if err != nil { return "", err } defer resp.Body.Close() if resp.StatusCode < 200 || resp.StatusCode >= 300 { return "", fmt.Errorf("logoapi status %d", resp.StatusCode) } var api logoAPIResponse if err := json.NewDecoder(resp.Body).Decode(&api); err != nil { return "", err } logoURL := strings.TrimSpace(api.LogoURLSVG) if logoURL == "" { logoURL = strings.TrimSpace(api.LogoURLPNG) } if logoURL == "" { logoURL = strings.TrimSpace(api.LogoURL) } if logoURL == "" { return "", fmt.Errorf("no logo url in api response") } req2, err := http.NewRequest("GET", logoURL, nil) if err != nil { return "", err } req2.Header.Set("User-Agent", "fotbal-club/logo-cache") req2.Header.Set("Accept", "*/*") resp2, err := client.Do(req2) if err != nil { return "", err } defer resp2.Body.Close() if resp2.StatusCode < 200 || resp2.StatusCode >= 300 { return "", fmt.Errorf("logo download status %d", resp2.StatusCode) } ct := strings.ToLower(strings.TrimSpace(resp2.Header.Get("Content-Type"))) ext := ".png" if strings.Contains(ct, "svg") || strings.HasSuffix(strings.ToLower(logoURL), ".svg") { ext = ".svg" } else if strings.Contains(ct, "webp") || strings.HasSuffix(strings.ToLower(logoURL), ".webp") { ext = ".webp" } else if strings.Contains(ct, "jpeg") || strings.HasSuffix(strings.ToLower(logoURL), ".jpg") || strings.HasSuffix(strings.ToLower(logoURL), ".jpeg") { ext = ".jpg" } destTmp := filepath.Join(destDir, "club-logo"+ext+".tmp") dest := filepath.Join(destDir, "club-logo"+ext) f, err := os.Create(destTmp) if err != nil { return "", err } _, copyErr := io.Copy(f, resp2.Body) closeErr := f.Close() if copyErr != nil { _ = os.Remove(destTmp) return "", copyErr } if closeErr != nil { _ = os.Remove(destTmp) return "", closeErr } _ = os.Rename(destTmp, dest) fi, _ := os.Stat(dest) mime := "image/png" if ext == ".svg" { mime = "image/svg+xml" } else if ext == ".jpg" || ext == ".jpeg" { mime = "image/jpeg" } else if ext == ".webp" { mime = "image/webp" } publicURL := "/uploads/" + filepath.ToSlash(filepath.Join("logos", "club", cid, "club-logo"+ext)) var existing models.UploadedFile if db != nil { if err := db.Where("file_path = ?", dest).First(&existing).Error; err != nil { uf := models.UploadedFile{ Filename: "club-logo" + ext, FilePath: dest, FileURL: publicURL, MimeType: mime, FileSize: 0, UploadedByID: nil, } if fi != nil { uf.FileSize = fi.Size() } _ = db.Create(&uf).Error } } return publicURL, nil }