This commit is contained in:
Tomáš Dvořák
2025-10-16 17:10:13 +02:00
parent f5e7be92c7
commit 35d0954afd
84 changed files with 9571 additions and 4668 deletions
+4
View File
@@ -2093,6 +2093,10 @@ func (bc *BaseController) SetupInitialize(c *gin.Context) {
}
logger.Info("Initial settings saved: club_id=%s club_name=%s gallery_url=%s gallery_label=%s", s.ClubID, s.ClubName, s.GalleryURL, s.GalleryLabel)
// Seed default homepage page elements with all available sections
bc.seedDefaultHomePageElements()
logger.Info("Default homepage page elements seeded")
// Run all setup operations asynchronously in background to provide immediate response
scheme := "http"
if c.Request.TLS != nil {
@@ -0,0 +1,75 @@
package controllers
import (
"fotbal-club/internal/models"
"fotbal-club/pkg/logger"
)
// seedDefaultHomePageElements creates default page element configurations for the homepage
// with all major sections visible by default (videos, gallery, matches, news, etc.)
func (bc *BaseController) seedDefaultHomePageElements() {
// Check if any homepage elements already exist
var count int64
if err := bc.DB.Model(&models.PageElementConfig{}).Where("page_type = ?", "homepage").Count(&count).Error; err != nil {
logger.Error("Failed to check existing page elements: %v", err)
return
}
if count > 0 {
logger.Info("Homepage page elements already exist, skipping seed")
return
}
// Define default homepage elements with all major sections visible
defaultElements := []models.PageElementConfig{
// Hero section - main featured content
{PageType: "homepage", ElementName: "hero", Variant: "grid", Visible: true, DisplayOrder: 1},
// News/Articles - latest articles
{PageType: "homepage", ElementName: "news", Variant: "grid", Visible: true, DisplayOrder: 2},
// Matches - upcoming and recent matches
{PageType: "homepage", ElementName: "matches", Variant: "compact", Visible: true, DisplayOrder: 3},
// Table - league standings
{PageType: "homepage", ElementName: "table", Variant: "split_news", Visible: true, DisplayOrder: 4},
// Videos - YouTube videos and highlights
{PageType: "homepage", ElementName: "videos", Variant: "grid", Visible: true, DisplayOrder: 5},
// Gallery - photo gallery
{PageType: "homepage", ElementName: "gallery", Variant: "grid", Visible: true, DisplayOrder: 6},
// Team - players and squad
{PageType: "homepage", ElementName: "team", Variant: "grid", Visible: true, DisplayOrder: 7},
// Activities - upcoming events
{PageType: "homepage", ElementName: "activities", Variant: "list", Visible: true, DisplayOrder: 8},
// Sponsors - partners and sponsors
{PageType: "homepage", ElementName: "sponsors", Variant: "grid", Visible: true, DisplayOrder: 9},
// Newsletter - newsletter signup
{PageType: "homepage", ElementName: "newsletter", Variant: "default", Visible: true, DisplayOrder: 10},
// Contact - contact information and map
{PageType: "homepage", ElementName: "contact", Variant: "combined", Visible: true, DisplayOrder: 11},
}
// Insert all default elements in a single transaction
tx := bc.DB.Begin()
for _, element := range defaultElements {
if err := tx.Create(&element).Error; err != nil {
logger.Error("Failed to create page element %s: %v", element.ElementName, err)
tx.Rollback()
return
}
}
if err := tx.Commit().Error; err != nil {
logger.Error("Failed to commit page elements transaction: %v", err)
return
}
logger.Info("Successfully seeded %d default homepage page elements", len(defaultElements))
}
+142
View File
@@ -0,0 +1,142 @@
package controllers
import (
"io/ioutil"
"net/http"
"path/filepath"
"strings"
"github.com/gin-gonic/gin"
)
type DocsController struct {
DocsPath string
}
func NewDocsController(docsPath string) *DocsController {
return &DocsController{
DocsPath: docsPath,
}
}
// GetDocFile serves a specific documentation file
func (dc *DocsController) GetDocFile(c *gin.Context) {
// Get the requested file path from the URL
docPath := c.Param("filepath")
// Security: Prevent directory traversal
if strings.Contains(docPath, "..") {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid file path"})
return
}
// Build full path
fullPath := filepath.Join(dc.DocsPath, docPath)
// Check if file exists and is a markdown file
if !strings.HasSuffix(fullPath, ".md") {
c.JSON(http.StatusBadRequest, gin.H{"error": "Only markdown files are allowed"})
return
}
// Read the file
content, err := ioutil.ReadFile(fullPath)
if err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "Documentation file not found"})
return
}
// Return the content as plain text
c.Header("Content-Type", "text/markdown; charset=utf-8")
c.String(http.StatusOK, string(content))
}
// ListDocFiles returns a list of available documentation files
func (dc *DocsController) ListDocFiles(c *gin.Context) {
files, err := ioutil.ReadDir(dc.DocsPath)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to read documentation directory"})
return
}
var docFiles []map[string]interface{}
for _, file := range files {
if !file.IsDir() && strings.HasSuffix(file.Name(), ".md") {
docFiles = append(docFiles, map[string]interface{}{
"name": file.Name(),
"path": "/DOCS/" + file.Name(),
"size": file.Size(),
"modified_at": file.ModTime(),
})
}
}
c.JSON(http.StatusOK, gin.H{
"files": docFiles,
"total": len(docFiles),
})
}
// SearchDocs searches through documentation files
func (dc *DocsController) SearchDocs(c *gin.Context) {
query := c.Query("q")
if query == "" {
c.JSON(http.StatusBadRequest, gin.H{"error": "Search query is required"})
return
}
query = strings.ToLower(query)
files, err := ioutil.ReadDir(dc.DocsPath)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to read documentation directory"})
return
}
var results []map[string]interface{}
for _, file := range files {
if file.IsDir() || !strings.HasSuffix(file.Name(), ".md") {
continue
}
fullPath := filepath.Join(dc.DocsPath, file.Name())
content, err := ioutil.ReadFile(fullPath)
if err != nil {
continue
}
contentLower := strings.ToLower(string(content))
nameLower := strings.ToLower(file.Name())
// Check if query matches filename or content
if strings.Contains(nameLower, query) || strings.Contains(contentLower, query) {
// Find context around match
index := strings.Index(contentLower, query)
start := 0
end := len(content)
if index > 100 {
start = index - 100
}
if index+len(query)+200 < len(content) {
end = index + len(query) + 200
}
excerpt := string(content[start:end])
results = append(results, map[string]interface{}{
"name": file.Name(),
"path": "/DOCS/" + file.Name(),
"excerpt": excerpt,
"matches": strings.Count(contentLower, query),
})
}
}
c.JSON(http.StatusOK, gin.H{
"results": results,
"total": len(results),
"query": query,
})
}