Files
MyClub/internal/controllers/sitemap_controller.go
T
Tomáš Dvořák 12cba639b9 upload
2025-10-16 13:32:05 +02:00

183 lines
4.9 KiB
Go

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())
}