mirror of
https://github.com/Dvorinka/MyClubServer.git
synced 2026-06-04 02:32:57 +00:00
hot fix #1
This commit is contained in:
@@ -515,17 +515,77 @@ func foldAccents(s string) string {
|
||||
// Optional query: q= filters by home/away/venue/competition
|
||||
func (bc *BaseController) GetMatchesHistory(c *gin.Context) {
|
||||
p := filepath.Join("cache", "prefetch", "events_past.json")
|
||||
f, err := os.Open(p)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusNoContent, gin.H{"message": "No cached past matches"})
|
||||
return
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
var matches []map[string]interface{}
|
||||
if err := json.NewDecoder(f).Decode(&matches); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Cannot parse cached past matches"})
|
||||
return
|
||||
if f, err := os.Open(p); err == nil {
|
||||
defer f.Close()
|
||||
if err := json.NewDecoder(f).Decode(&matches); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Cannot parse cached past matches"})
|
||||
return
|
||||
}
|
||||
} else {
|
||||
p2 := filepath.Join("cache", "prefetch", "facr_club_info.json")
|
||||
if f2, err2 := os.Open(p2); err2 == nil {
|
||||
defer f2.Close()
|
||||
var facr struct {
|
||||
Competitions []struct {
|
||||
Matches []struct {
|
||||
MatchID string `json:"match_id"`
|
||||
Home string `json:"home"`
|
||||
Away string `json:"away"`
|
||||
Venue string `json:"venue"`
|
||||
DateTime string `json:"date_time"`
|
||||
Score string `json:"score"`
|
||||
HomeLogoURL string `json:"home_logo_url"`
|
||||
AwayLogoURL string `json:"away_logo_url"`
|
||||
HomeID string `json:"home_id"`
|
||||
AwayID string `json:"away_id"`
|
||||
HomeTeamID string `json:"home_team_id"`
|
||||
AwayTeamID string `json:"away_team_id"`
|
||||
} `json:"matches"`
|
||||
} `json:"competitions"`
|
||||
}
|
||||
if err := json.NewDecoder(f2).Decode(&facr); err == nil {
|
||||
now := time.Now()
|
||||
for _, c := range facr.Competitions {
|
||||
for _, m := range c.Matches {
|
||||
dt := strings.TrimSpace(m.DateTime)
|
||||
if dt == "" {
|
||||
continue
|
||||
}
|
||||
ts, perr := time.ParseInLocation("02.01.2006 15:04", dt, time.Local)
|
||||
if perr != nil || ts.After(now) {
|
||||
continue
|
||||
}
|
||||
row := map[string]interface{}{
|
||||
"match_id": m.MatchID,
|
||||
"home": m.Home,
|
||||
"away": m.Away,
|
||||
"venue": m.Venue,
|
||||
"date_time": m.DateTime,
|
||||
"score": m.Score,
|
||||
"home_logo_url": m.HomeLogoURL,
|
||||
"away_logo_url": m.AwayLogoURL,
|
||||
}
|
||||
if m.HomeTeamID != "" {
|
||||
row["home_team_id"] = m.HomeTeamID
|
||||
} else if m.HomeID != "" {
|
||||
row["home_team_id"] = m.HomeID
|
||||
row["home_id"] = m.HomeID
|
||||
}
|
||||
if m.AwayTeamID != "" {
|
||||
row["away_team_id"] = m.AwayTeamID
|
||||
} else if m.AwayID != "" {
|
||||
row["away_team_id"] = m.AwayID
|
||||
row["away_id"] = m.AwayID
|
||||
}
|
||||
matches = append(matches, row)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
c.JSON(http.StatusNoContent, gin.H{"message": "No cached past matches"})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Apply overrides (same as in GetMatches)
|
||||
@@ -922,7 +982,7 @@ func (bc *BaseController) GetZoneramaAlbum(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
req.Header.Set("User-Agent", "fotbal-club/zonerama-proxy")
|
||||
client := &http.Client{Timeout: 25 * time.Second}
|
||||
client := &http.Client{Timeout: 60 * time.Second}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadGateway, gin.H{"error": err.Error()})
|
||||
@@ -1907,7 +1967,6 @@ func (bc *BaseController) GetCompetitionAliases(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, items)
|
||||
}
|
||||
|
||||
// Admin: create or replace alias by code (idempotent)
|
||||
func (bc *BaseController) PutCompetitionAlias(c *gin.Context) {
|
||||
code := strings.TrimSpace(c.Param("code"))
|
||||
if code == "" {
|
||||
@@ -1928,7 +1987,7 @@ func (bc *BaseController) PutCompetitionAlias(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
var item models.CompetitionAlias
|
||||
if err := bc.DB.Where("code = ?", code).First(&item).Error; err != nil {
|
||||
if err := bc.DB.Unscoped().Where("code = ?", code).First(&item).Error; err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
item = models.CompetitionAlias{Code: code}
|
||||
} else {
|
||||
@@ -1947,6 +2006,12 @@ func (bc *BaseController) PutCompetitionAlias(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if item.DeletedAt.Valid {
|
||||
if err := bc.DB.Unscoped().Model(&item).Update("deleted_at", nil).Error; err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Nelze obnovit alias"})
|
||||
return
|
||||
}
|
||||
}
|
||||
if err := bc.DB.Save(&item).Error; err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Nelze uložit alias"})
|
||||
return
|
||||
@@ -1964,7 +2029,7 @@ func (bc *BaseController) DeleteCompetitionAlias(c *gin.Context) {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Chyba code"})
|
||||
return
|
||||
}
|
||||
if err := bc.DB.Where("code = ?", code).Delete(&models.CompetitionAlias{}).Error; err != nil {
|
||||
if err := bc.DB.Unscoped().Where("code = ?", code).Delete(&models.CompetitionAlias{}).Error; err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Nelze smazat alias"})
|
||||
return
|
||||
}
|
||||
@@ -3056,8 +3121,15 @@ func (bc *BaseController) SetupInitialize(c *gin.Context) {
|
||||
}
|
||||
services.StartErrorReviewAutoRegister(bc.DB)
|
||||
if strings.TrimSpace(s.ClubID) != "" {
|
||||
if url, err := services.CacheClubLogo(bc.DB, strings.TrimSpace(s.ClubID)); err == nil && strings.TrimSpace(url) != "" {
|
||||
_ = bc.DB.Model(&models.Settings{}).Where("id = ?", s.ID).Update("club_logo_url", url).Error
|
||||
if clubInfo, err := services.CacheClubLogoAndName(bc.DB, strings.TrimSpace(s.ClubID)); err == nil {
|
||||
if strings.TrimSpace(clubInfo.LogoURL) != "" {
|
||||
_ = bc.DB.Model(&models.Settings{}).Where("id = ?", s.ID).Update("club_logo_url", clubInfo.LogoURL).Error
|
||||
}
|
||||
// Update club name if it's empty and we got one from logoapi
|
||||
if strings.TrimSpace(s.ClubName) == "" && strings.TrimSpace(clubInfo.ClubName) != "" {
|
||||
_ = bc.DB.Model(&models.Settings{}).Where("id = ?", s.ID).Update("club_name", clubInfo.ClubName).Error
|
||||
s.ClubName = clubInfo.ClubName // Update local variable for logging
|
||||
}
|
||||
}
|
||||
}
|
||||
go func(snap models.Settings) {
|
||||
@@ -3462,8 +3534,15 @@ func (bc *BaseController) SetupInitialize(c *gin.Context) {
|
||||
services.StartErrorReviewAutoRegister(bc.DB)
|
||||
if strings.TrimSpace(s.ClubID) != "" {
|
||||
go func(id uint, clubID string) {
|
||||
if url, err := services.CacheClubLogo(bc.DB, strings.TrimSpace(clubID)); err == nil && strings.TrimSpace(url) != "" {
|
||||
_ = bc.DB.Model(&models.Settings{}).Where("id = ?", id).Update("club_logo_url", url).Error
|
||||
if clubInfo, err := services.CacheClubLogoAndName(bc.DB, strings.TrimSpace(clubID)); err == nil {
|
||||
if strings.TrimSpace(clubInfo.LogoURL) != "" {
|
||||
_ = bc.DB.Model(&models.Settings{}).Where("id = ?", id).Update("club_logo_url", clubInfo.LogoURL).Error
|
||||
}
|
||||
// Update club name if it's empty and we got one from logoapi
|
||||
var currentSettings models.Settings
|
||||
if bc.DB.First(¤tSettings, id).Error == nil && strings.TrimSpace(currentSettings.ClubName) == "" && strings.TrimSpace(clubInfo.ClubName) != "" {
|
||||
_ = bc.DB.Model(&models.Settings{}).Where("id = ?", id).Update("club_name", clubInfo.ClubName).Error
|
||||
}
|
||||
}
|
||||
}(s.ID, s.ClubID)
|
||||
}
|
||||
@@ -4130,8 +4209,15 @@ func (bc *BaseController) UpdateSettings(c *gin.Context) {
|
||||
services.StartErrorReviewAutoRegister(bc.DB)
|
||||
if strings.TrimSpace(s.ClubID) != "" && (strings.HasPrefix(strings.ToLower(strings.TrimSpace(s.ClubLogoURL)), "http://") || strings.HasPrefix(strings.ToLower(strings.TrimSpace(s.ClubLogoURL)), "https://") || strings.TrimSpace(s.ClubLogoURL) == "") {
|
||||
go func(id uint, clubID string) {
|
||||
if url, err := services.CacheClubLogo(bc.DB, strings.TrimSpace(clubID)); err == nil && strings.TrimSpace(url) != "" {
|
||||
_ = bc.DB.Model(&models.Settings{}).Where("id = ?", id).Update("club_logo_url", url).Error
|
||||
if clubInfo, err := services.CacheClubLogoAndName(bc.DB, strings.TrimSpace(clubID)); err == nil {
|
||||
if strings.TrimSpace(clubInfo.LogoURL) != "" {
|
||||
_ = bc.DB.Model(&models.Settings{}).Where("id = ?", id).Update("club_logo_url", clubInfo.LogoURL).Error
|
||||
}
|
||||
// Update club name if it's empty and we got one from logoapi
|
||||
var currentSettings models.Settings
|
||||
if bc.DB.First(¤tSettings, id).Error == nil && strings.TrimSpace(currentSettings.ClubName) == "" && strings.TrimSpace(clubInfo.ClubName) != "" {
|
||||
_ = bc.DB.Model(&models.Settings{}).Where("id = ?", id).Update("club_name", clubInfo.ClubName).Error
|
||||
}
|
||||
}
|
||||
}(s.ID, s.ClubID)
|
||||
}
|
||||
@@ -4264,7 +4350,9 @@ func (bc *BaseController) GetPublicSettings(c *gin.Context) {
|
||||
"club_logo_url": s.ClubLogoURL,
|
||||
"club_url": s.ClubURL,
|
||||
// Runtime flags (env-based)
|
||||
"premium": config.AppConfig.Premium,
|
||||
"premium": config.AppConfig.Premium,
|
||||
"club_data_mode": config.AppConfig.ClubDataMode,
|
||||
"eshop_enabled": config.AppConfig.EshopEnabled,
|
||||
|
||||
// Theme
|
||||
"primary_color": s.PrimaryColor,
|
||||
@@ -4361,6 +4449,16 @@ func (bc *BaseController) GetSettings(c *gin.Context) {
|
||||
}
|
||||
s.LoadCustomNav()
|
||||
s.LoadVideosOverrides()
|
||||
// Decode manual videos for admin payload (populate transient exported fields)
|
||||
if strings.TrimSpace(s.VideosJSON) != "" {
|
||||
var vids []string
|
||||
_ = json.Unmarshal([]byte(s.VideosJSON), &vids)
|
||||
s.Videos = vids
|
||||
}
|
||||
if strings.TrimSpace(s.VideosItemsJSON) != "" {
|
||||
// Unmarshal directly into the transient anonymous struct field type
|
||||
_ = json.Unmarshal([]byte(s.VideosItemsJSON), &s.VideosItems)
|
||||
}
|
||||
// derive map form for admin consumers
|
||||
mv := map[string]string{}
|
||||
if len(s.VideosOverrides) > 0 {
|
||||
@@ -5169,7 +5267,7 @@ func (bc *BaseController) UploadImage(c *gin.Context) {
|
||||
}
|
||||
name := strings.TrimSpace(f.Filename)
|
||||
ext := strings.ToLower(filepath.Ext(name))
|
||||
// Allow images, PDFs, Office docs, text, archives, and common media
|
||||
// Allow images, PDFs, Office docs, text, archives, and common media (including webm audio)
|
||||
allowed := map[string]bool{
|
||||
// Images
|
||||
".jpg": true, ".jpeg": true, ".png": true, ".gif": true, ".webp": true, ".svg": true,
|
||||
@@ -5178,7 +5276,7 @@ func (bc *BaseController) UploadImage(c *gin.Context) {
|
||||
// Archives
|
||||
".zip": true, ".rar": true, ".7z": true, ".tar": true, ".gz": true,
|
||||
// Media
|
||||
".mp4": true, ".avi": true, ".mov": true, ".mp3": true, ".wav": true,
|
||||
".mp4": true, ".avi": true, ".mov": true, ".mp3": true, ".wav": true, ".webm": true,
|
||||
}
|
||||
if !allowed[ext] {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "unsupported file type"})
|
||||
@@ -5223,8 +5321,10 @@ func (bc *BaseController) UploadImage(c *gin.Context) {
|
||||
validCT = strings.HasPrefix(dl, "text/") || dl == "application/octet-stream"
|
||||
case ".mp4", ".avi", ".mov":
|
||||
validCT = strings.HasPrefix(dl, "video/") || dl == "application/octet-stream"
|
||||
case ".mp3", ".wav":
|
||||
validCT = strings.HasPrefix(dl, "audio/") || dl == "application/octet-stream"
|
||||
case ".mp3", ".wav", ".webm":
|
||||
// Some browsers label MediaRecorder audio-only blobs as video/webm,
|
||||
// so allow both audio/* and video/* for webm uploads in addition to a generic octet-stream.
|
||||
validCT = strings.HasPrefix(dl, "audio/") || strings.HasPrefix(dl, "video/") || dl == "application/octet-stream"
|
||||
default:
|
||||
validCT = strings.HasPrefix(dl, "image/")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user