package controllers import ( "encoding/xml" "fmt" "net/http" "strings" "time" "fotbal-club/internal/models" "github.com/gin-gonic/gin" "gorm.io/gorm" ) // SitemapController handles sitemap generation type SitemapController struct { DB *gorm.DB } // URLSet represents the root sitemap element type URLSet struct { XMLName xml.Name `xml:"urlset"` XMLNS string `xml:"xmlns,attr"` URLs []URL `xml:"url"` } // URL represents a single URL entry in sitemap type URL struct { Loc string `xml:"loc"` LastMod string `xml:"lastmod,omitempty"` ChangeFreq string `xml:"changefreq,omitempty"` Priority float32 `xml:"priority,omitempty"` } // GetSitemap generates and returns sitemap.xml func (sc *SitemapController) GetSitemap(c *gin.Context) { // Get base URL from request or settings scheme := "https" if c.Request.TLS == nil && c.Request.Header.Get("X-Forwarded-Proto") != "https" { scheme = "http" } baseURL := fmt.Sprintf("%s://%s", scheme, c.Request.Host) // Initialize sitemap sitemap := URLSet{ XMLNS: "http://www.sitemaps.org/schemas/sitemap/0.9", URLs: []URL{}, } // Add homepage sitemap.URLs = append(sitemap.URLs, URL{ Loc: baseURL + "/", ChangeFreq: "daily", Priority: 1.0, }) // Add static pages staticPages := []struct { path string freq string priority float32 }{ {"/blog", "daily", 0.9}, {"/zapasy", "daily", 0.9}, {"/tabulky", "weekly", 0.8}, {"/hraci", "weekly", 0.8}, {"/o-klubu", "monthly", 0.7}, {"/kontakt", "monthly", 0.6}, {"/galerie", "weekly", 0.7}, {"/videa", "weekly", 0.7}, {"/sponzori", "monthly", 0.5}, {"/kalendar", "weekly", 0.7}, {"/aktivity", "weekly", 0.7}, {"/obleceni", "monthly", 0.6}, {"/ankety", "weekly", 0.6}, } for _, page := range staticPages { sitemap.URLs = append(sitemap.URLs, URL{ Loc: baseURL + page.path, ChangeFreq: page.freq, Priority: page.priority, }) } // Add published articles var articles []models.Article if err := sc.DB.Where("published = ?", true). Order("published_at DESC, created_at DESC"). Limit(1000). // Reasonable limit Find(&articles).Error; err == nil { for _, article := range articles { lastMod := article.UpdatedAt.Format("2006-01-02") if !article.PublishedAt.IsZero() { lastMod = article.PublishedAt.Format("2006-01-02") } // Add both slug and ID URLs if article.Slug != "" { sitemap.URLs = append(sitemap.URLs, URL{ Loc: baseURL + "/articles/slug/" + article.Slug, LastMod: lastMod, ChangeFreq: "weekly", Priority: 0.8, }) } sitemap.URLs = append(sitemap.URLs, URL{ Loc: baseURL + fmt.Sprintf("/articles/%d", article.ID), LastMod: lastMod, ChangeFreq: "weekly", Priority: 0.7, }) } } // Add players var players []models.Player if err := sc.DB.Order("created_at DESC").Limit(500).Find(&players).Error; err == nil { for _, player := range players { sitemap.URLs = append(sitemap.URLs, URL{ Loc: baseURL + fmt.Sprintf("/hraci/%d", player.ID), LastMod: player.UpdatedAt.Format("2006-01-02"), ChangeFreq: "monthly", Priority: 0.6, }) } } // Generate XML output, err := xml.MarshalIndent(sitemap, "", " ") if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to generate sitemap"}) return } // Set headers and return c.Header("Content-Type", "application/xml; charset=utf-8") c.Header("Cache-Control", "public, max-age=3600") // Cache for 1 hour c.String(http.StatusOK, xml.Header+string(output)) } // GetRobotsTxt returns robots.txt with sitemap reference func (sc *SitemapController) GetRobotsTxt(c *gin.Context) { scheme := "https" if c.Request.TLS == nil && c.Request.Header.Get("X-Forwarded-Proto") != "https" { scheme = "http" } baseURL := fmt.Sprintf("%s://%s", scheme, c.Request.Host) // Check if indexing is disabled in settings var settings models.Settings allowIndex := true if err := sc.DB.First(&settings).Error; err == nil { allowIndex = settings.EnableIndexing } var robots strings.Builder robots.WriteString("# robots.txt for " + c.Request.Host + "\n") robots.WriteString("# Generated: " + time.Now().Format(time.RFC1123) + "\n\n") if !allowIndex { // Block all robots if indexing disabled robots.WriteString("User-agent: *\n") robots.WriteString("Disallow: /\n") } else { // Allow most content, block admin and API robots.WriteString("User-agent: *\n") robots.WriteString("Disallow: /admin/\n") robots.WriteString("Disallow: /api/\n") robots.WriteString("Disallow: /login\n") robots.WriteString("Disallow: /setup\n") robots.WriteString("Allow: /\n\n") // Add sitemap reference robots.WriteString("Sitemap: " + baseURL + "/sitemap.xml\n") } c.Header("Content-Type", "text/plain; charset=utf-8") c.Header("Cache-Control", "public, max-age=86400") // Cache for 24 hours c.String(http.StatusOK, robots.String()) }