Files
vindn.de/backend/repositories/EventRepository.go
T

160 lines
4.4 KiB
Go

package repositories
import (
"crypto/md5"
"errors"
"fmt"
"io"
"log"
"net/http"
"os"
"path/filepath"
"strings"
"whimsoc/vindn-backend/models"
"gorm.io/gorm"
"gorm.io/gorm/clause"
)
type EventRepository interface {
Save(event *models.Event) error
FindAll() ([]models.Event, error)
SoftDeleteEndedBefore(cutoffDate string) (int64, error)
}
type eventRepository struct {
db *gorm.DB
}
func NewEventRepository(db *gorm.DB) EventRepository {
return &eventRepository{db}
}
func (r *eventRepository) Save(event *models.Event) error {
if event.Geo != nil && event.Geo.Main != nil {
event.Latitude = event.Geo.Main.Latitude
event.Longitude = event.Geo.Main.Longitude
}
var categories []models.Category
for _, name := range event.CategoryNames {
if name == "" {
continue
}
var cat models.Category
if err := r.db.FirstOrCreate(&cat, models.Category{Name: name}).Error; err != nil {
return err
}
categories = append(categories, cat)
}
event.Categories = categories
var mediaObjects []models.MediaObject
for _, rawMediaObject := range event.RawMediaObjects {
if rawMediaObject.Name == "" {
continue
}
var mediaObject models.MediaObject
h := md5.New()
io.WriteString(h, rawMediaObject.Rel)
io.WriteString(h, rawMediaObject.Name)
io.WriteString(h, rawMediaObject.URL)
if err := r.db.FirstOrCreate(&mediaObject, models.MediaObject{
Hash: fmt.Sprintf("%x", h.Sum(nil)),
RefID: rawMediaObject.RefID,
Name: rawMediaObject.Name,
URL: rawMediaObject.URL,
Type: rawMediaObject.Type,
Rel: rawMediaObject.Rel,
Alt: rawMediaObject.Alt,
Source: rawMediaObject.Source,
Description: rawMediaObject.Description,
Prio: rawMediaObject.Prio,
AiOptimized: rawMediaObject.AiOptimized,
License: rawMediaObject.License,
Ext: filepath.Ext(rawMediaObject.URL),
}).Error; err != nil {
return err
}
if mediaObject.Type != "" {
mediaType, _, mediaTypeFound := strings.Cut(mediaObject.Type, "/")
if err := r.saveMediaFile(mediaObject.Hash, mediaObject.URL, mediaType, mediaObject.Ext); mediaTypeFound && err != nil {
log.Fatal(err)
}
}
mediaObjects = append(mediaObjects, mediaObject)
}
event.MediaObjects = mediaObjects
return r.db.Clauses(clause.OnConflict{
Columns: []clause.Column{{Name: "external_id"}},
UpdateAll: true,
}).Create(event).Error
}
func (r *eventRepository) FindAll() ([]models.Event, error) {
var events []models.Event
result := r.db.Find(&events)
return events, result.Error
}
// SoftDeleteEndedBefore marks events whose end_date is before cutoffDate
// (an exclusive "YYYY-MM-DD" boundary, the same lexicographic string
// comparison the frontend uses for its own date filtering) as deleted.
// .Delete() on a gorm.Model sets deleted_at rather than removing the row,
// consistent with how the frontend already treats soft-deleted events.
// Events with a blank end_date are left alone — there's nothing to
// compare, so leaving them is safer than guessing they're stale.
func (r *eventRepository) SoftDeleteEndedBefore(cutoffDate string) (int64, error) {
result := r.db.Where("end_date <> '' AND end_date < ?", cutoffDate).Delete(&models.Event{})
return result.RowsAffected, result.Error
}
func (r *eventRepository) saveMediaFile(filename string, url string, mediaType string, extension string) error {
mediaFilePath := fmt.Sprintf("./public/storage/%s/%s%s", strings.ToLower(mediaType), filename, extension)
log.Printf("Get file %s and save it to %s", url, mediaFilePath)
// Check if the directory exists
if _, err := os.Stat(filepath.Dir(mediaFilePath)); os.IsNotExist(err) {
// Create the directory if it doesn't exist
err := os.MkdirAll(filepath.Dir(mediaFilePath), 0755) // 0755 is a common permission for directories
if err != nil {
return fmt.Errorf("failed to create directory: %v", err)
}
}
fileInfo, err := os.Stat(mediaFilePath)
if errors.Is(err, os.ErrNotExist) || fileInfo.Size() == 0 {
// Create the file
out, err := os.Create(mediaFilePath)
if err != nil {
return err
}
defer out.Close()
// Get the data
resp, err := http.Get(url)
if err != nil {
return err
}
defer resp.Body.Close()
// Write the body to file
_, err = io.Copy(out, resp.Body)
if err != nil {
return err
}
} else {
log.Printf("File already exists, do not download again. Stats: Name: %s, Size: %d", fileInfo.Name(), fileInfo.Size())
}
return nil
}