Files
MyClub/internal/controllers/event_controller.go
T
Tomas Dvorak dfc079288f hot fix #1
2026-01-26 08:13:18 +01:00

262 lines
8.2 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package controllers
import (
"fotbal-club/internal/models"
"fotbal-club/internal/services"
"net/http"
"time"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
)
type EventController struct{ DB *gorm.DB }
// GetEventByID returns a single event by its ID (public; returns only public events unless owner)
func (ctrl *EventController) GetEventByID(c *gin.Context) {
id := c.Param("id")
var ev models.Event
if err := ctrl.DB.Preload("Attachments").First(&ev, id).Error; err != nil {
if err == gorm.ErrRecordNotFound {
c.JSON(http.StatusNotFound, gin.H{"error": "Event not found"})
return
}
c.JSON(http.StatusInternalServerError, gin.H{"error": "Database error"})
return
}
// If not public, allow only owner (when identified upstream)
if !ev.IsPublic {
if roleVal, hasRole := c.Get("userRole"); hasRole {
if role, _ := roleVal.(string); role == "admin" {
c.JSON(http.StatusOK, ev)
return
}
}
if userID, exists := c.Get("userID"); !exists || ev.CreatedByID != userID {
c.JSON(http.StatusForbidden, gin.H{"error": "Not allowed"})
return
}
}
c.JSON(http.StatusOK, ev)
}
type EventInput struct {
Title string `json:"title" binding:"required"`
Description string `json:"description"`
StartTime time.Time `json:"start_time" binding:"required"`
EndTime *time.Time `json:"end_time"`
Location string `json:"location"`
Type string `json:"type" binding:"required,oneof=match training meeting other"`
IsPublic bool `json:"is_public"`
CategoryName string `json:"category_name"`
ImageURL string `json:"image_url"`
FileURL string `json:"file_url"`
YoutubeURL string `json:"youtube_url"`
Latitude *float64 `json:"latitude"`
Longitude *float64 `json:"longitude"`
Attachments []struct {
Name string `json:"name"`
URL string `json:"url"`
MimeType string `json:"mime_type"`
Size int64 `json:"size"`
} `json:"attachments"`
}
func (ctrl *EventController) CreateEvent(c *gin.Context) {
// Ensure latest schema (adds columns if missing)
_ = ctrl.DB.AutoMigrate(&models.Event{}, &models.EventAttachment{})
var input EventInput
if err := c.ShouldBindJSON(&input); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
userID, _ := c.Get("userID")
imageURL := input.ImageURL
if imageURL == "" && isXAIEnabled() {
prompt := "Titulní obrázek pro klubovou událost na oficiálním webu fotbalového klubu. Název události: \"" + input.Title + "\". Typ: " + string(models.EventType(input.Type)) + ". Zaměř se na prostředí klubu stadion, tréninkové hřiště a fanoušky v klubových barvách. Styl: realistický, moderní, sportovní, bez textu, široký banner v poměru 16:9."
if input.CategoryName != "" {
prompt += " Téma / soutěž: " + input.CategoryName + "."
}
if urls, _, err := callXAIImage(getXAIImageModel(), prompt, "1920x1080", 1); err == nil && len(urls) > 0 && urls[0] != "" {
imageURL = urls[0]
}
}
if imageURL == "" {
imageURL = "/dist/img/logo-club-empty.svg"
}
event := models.Event{
Title: input.Title,
Description: input.Description,
StartTime: input.StartTime,
EndTime: input.EndTime,
Location: input.Location,
Type: models.EventType(input.Type),
IsPublic: input.IsPublic,
CreatedByID: userID.(uint),
CategoryName: input.CategoryName,
ImageURL: imageURL,
FileURL: input.FileURL,
YoutubeURL: input.YoutubeURL,
Latitude: input.Latitude,
Longitude: input.Longitude,
}
if err := ctrl.DB.Create(&event).Error; err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create event"})
return
}
// Create attachments if any
if len(input.Attachments) > 0 {
var atts []models.EventAttachment
for _, a := range input.Attachments {
if a.URL == "" {
continue
}
atts = append(atts, models.EventAttachment{EventID: event.ID, Name: a.Name, URL: a.URL, MimeType: a.MimeType, Size: a.Size})
}
if len(atts) > 0 {
if err := ctrl.DB.Create(&atts).Error; err != nil {
// non-fatal
}
}
}
// Reload with attachments
var out models.Event
_ = ctrl.DB.Preload("Attachments").First(&out, event.ID).Error
// Track file usage
fileTracker := services.NewFileTracker(ctrl.DB)
go fileTracker.TrackEventFiles(&out)
c.JSON(http.StatusCreated, out)
}
func (ctrl *EventController) GetEvents(c *gin.Context) {
var events []models.Event
query := ctrl.DB.Preload("Attachments")
// Admin sees all events
if roleVal, hasRole := c.Get("userRole"); hasRole {
if role, _ := roleVal.(string); role == "admin" {
if err := query.Find(&events).Error; err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to fetch events"})
return
}
c.JSON(http.StatusOK, events)
return
}
}
if userID, exists := c.Get("userID"); !exists {
query = query.Where("is_public = ?", true)
} else {
query = query.Where("created_by_id = ? OR is_public = ?", userID, true)
}
if err := query.Find(&events).Error; err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to fetch events"})
return
}
c.JSON(http.StatusOK, events)
}
func (ctrl *EventController) GetUpcomingEvents(c *gin.Context) {
var events []models.Event
query := ctrl.DB.Preload("Attachments").Where("start_time >= ?", time.Now()).Order("start_time ASC").Limit(5)
// Admin sees all upcoming events
if roleVal, hasRole := c.Get("userRole"); hasRole {
if role, _ := roleVal.(string); role == "admin" {
if err := query.Find(&events).Error; err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to fetch events"})
return
}
c.JSON(http.StatusOK, events)
return
}
}
if userID, exists := c.Get("userID"); !exists {
query = query.Where("is_public = ?", true)
} else {
query = query.Where("created_by_id = ? OR is_public = ?", userID, true)
}
if err := query.Find(&events).Error; err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to fetch events"})
return
}
c.JSON(http.StatusOK, events)
}
// UpdateEvent updates an existing event (protected)
func (ctrl *EventController) UpdateEvent(c *gin.Context) {
// Ensure latest schema (adds columns if missing)
_ = ctrl.DB.AutoMigrate(&models.Event{}, &models.EventAttachment{})
id := c.Param("id")
var ev models.Event
if err := ctrl.DB.First(&ev, id).Error; err != nil {
if err == gorm.ErrRecordNotFound {
c.JSON(http.StatusNotFound, gin.H{"error": "Event not found"})
return
}
c.JSON(http.StatusInternalServerError, gin.H{"error": "Database error"})
return
}
var input EventInput
if err := c.ShouldBindJSON(&input); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
ev.Title = input.Title
ev.Description = input.Description
ev.StartTime = input.StartTime
ev.EndTime = input.EndTime
ev.Location = input.Location
ev.Type = models.EventType(input.Type)
ev.IsPublic = input.IsPublic
ev.CategoryName = input.CategoryName
ev.ImageURL = input.ImageURL
ev.FileURL = input.FileURL
ev.YoutubeURL = input.YoutubeURL
ev.Latitude = input.Latitude
ev.Longitude = input.Longitude
if err := ctrl.DB.Save(&ev).Error; err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to update event"})
return
}
// Replace attachments (simple strategy)
if err := ctrl.DB.Where("event_id = ?", ev.ID).Delete(&models.EventAttachment{}).Error; err == nil {
if len(input.Attachments) > 0 {
var atts []models.EventAttachment
for _, a := range input.Attachments {
if a.URL == "" {
continue
}
atts = append(atts, models.EventAttachment{EventID: ev.ID, Name: a.Name, URL: a.URL, MimeType: a.MimeType, Size: a.Size})
}
if len(atts) > 0 {
_ = ctrl.DB.Create(&atts).Error
}
}
}
var out models.Event
_ = ctrl.DB.Preload("Attachments").First(&out, ev.ID).Error
// Track file usage
fileTracker := services.NewFileTracker(ctrl.DB)
go fileTracker.TrackEventFiles(&out)
c.JSON(http.StatusOK, out)
}
// DeleteEvent removes an event (protected)
func (ctrl *EventController) DeleteEvent(c *gin.Context) {
id := c.Param("id")
if err := ctrl.DB.Delete(&models.Event{}, id).Error; err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to delete event"})
return
}
c.JSON(http.StatusOK, gin.H{"ok": true})
}