package controllers import ( "net/http" "time" "github.com/gin-gonic/gin" "gorm.io/gorm" "fotbal-club/internal/models" "fotbal-club/internal/services" ) 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 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") 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: input.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") 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) 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}) }