From 781212f70608372eb71dbe69e2d6826f1207d5d9 Mon Sep 17 00:00:00 2001 From: Xor290 Date: Mon, 29 Jun 2026 18:56:00 +0200 Subject: [PATCH] chore: fix latest build --- backend/gestion/db/db_init.go | 6 ++++++ backend/gestion/db/db_media.go | 23 ++++++++++++++--------- backend/gestion/handlers/product.go | 2 +- backend/gestion/main.go | 2 +- backend/gestion/services/s3.go | 6 ++---- 5 files changed, 24 insertions(+), 15 deletions(-) diff --git a/backend/gestion/db/db_init.go b/backend/gestion/db/db_init.go index b06e9c70..e0731e6e 100644 --- a/backend/gestion/db/db_init.go +++ b/backend/gestion/db/db_init.go @@ -294,6 +294,11 @@ func InitDB() *Database { log.Fatalf("❌ Erreur migration contacts: %v", err) } + // Migration: clé RustFS pour les médias (stockage objet) + if _, err = database.Exec(`ALTER TABLE media ADD COLUMN IF NOT EXISTS key TEXT NOT NULL DEFAULT ''`); err != nil { + log.Fatalf("❌ Erreur migration media.key: %v", err) + } + // Lancer le nettoyage périodique des tokens expirés go database.cleanExpiredTokensPeriodically() @@ -399,6 +404,7 @@ func (db *Database) createTables() error { product_id INTEGER NOT NULL REFERENCES products(id) ON DELETE CASCADE, url TEXT NOT NULL, type VARCHAR(50) NOT NULL, + key TEXT NOT NULL DEFAULT '', created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP );`, diff --git a/backend/gestion/db/db_media.go b/backend/gestion/db/db_media.go index 60548f18..1c2b4b8a 100644 --- a/backend/gestion/db/db_media.go +++ b/backend/gestion/db/db_media.go @@ -59,8 +59,8 @@ func validateMediaURL(url string) error { if strings.Contains(url, "..") || strings.Contains(url, "...") || strings.Contains(url, "..//") { return fmt.Errorf("path traversal détecté dans l'URL") } - if !strings.HasPrefix(url, "/uploads/") { - return fmt.Errorf("URL doit commencer par /uploads/") + if !strings.HasPrefix(url, "/uploads/") && !strings.HasPrefix(url, "/media/") { + return fmt.Errorf("URL doit commencer par /uploads/ ou /media/") } dangerousChars := []string{"<", ">", "\"", "'", ";", "|", "&", "$", "`", "\\"} for _, char := range dangerousChars { @@ -93,6 +93,11 @@ func (d *Database) CreateMedia(media any) error { mediaType := m.GetType() mediaURL := m.GetURL() + mediaKey := "" + if mediaPtr, isPtr := media.(*models.Media); isPtr { + mediaKey = mediaPtr.Key + } + if err := validateProductID(productID); err != nil { log.Printf("❌ [CreateMedia] %v", err) return err @@ -106,14 +111,14 @@ func (d *Database) CreateMedia(media any) error { return err } - if err := d.InsertMedia(m, productID, mediaURL, mediaType); err != nil { + if err := d.InsertMedia(m, productID, mediaURL, mediaType, mediaKey); err != nil { log.Printf("❌ [InsertMedia] %v", err) return err } return nil } -func (d *Database) InsertMedia(m any, productID int, mediaURL any, mediaType string) error { +func (d *Database) InsertMedia(m any, productID int, mediaURL any, mediaType string, key string) error { var exists bool if err := d.GDB.Raw(`SELECT EXISTS(SELECT 1 FROM products WHERE id = ?)`, productID).Scan(&exists).Error; err != nil { log.Printf("❌ [CreateMedia] Erreur vérification produit: %v", err) @@ -128,9 +133,9 @@ func (d *Database) InsertMedia(m any, productID int, mediaURL any, mediaType str ID int `gorm:"column:id"` } err := d.GDB.Raw(` - INSERT INTO media (product_id, url, type, created_at) - VALUES (?, ?, ?, ?) RETURNING id`, - productID, mediaURL, mediaType, time.Now(), + INSERT INTO media (product_id, url, type, key, created_at) + VALUES (?, ?, ?, ?, ?) RETURNING id`, + productID, mediaURL, mediaType, key, time.Now(), ).Scan(&result).Error if err != nil { log.Printf("❌ [CreateMedia] Erreur INSERT: %v", err) @@ -154,7 +159,7 @@ func (d *Database) GetMediaByID(mediaID int) (*models.Media, error) { var media models.Media err := d.GDB.Raw(` - SELECT id, product_id, url, type, created_at + SELECT id, product_id, url, type, key, created_at FROM media WHERE id = ?`, mediaID).Scan(&media).Error if err != nil { log.Printf("❌ [GetMediaByID] Erreur query: %v", err) @@ -179,7 +184,7 @@ func (d *Database) GetMediaByProductID(productID int) ([]models.Media, error) { var mediaList []models.Media err := d.GDB.Raw(` - SELECT id, product_id, url, type, created_at + SELECT id, product_id, url, type, key, created_at FROM media WHERE product_id = ? ORDER BY id ASC`, productID).Scan(&mediaList).Error if err != nil { diff --git a/backend/gestion/handlers/product.go b/backend/gestion/handlers/product.go index 5059c355..9ea777ca 100644 --- a/backend/gestion/handlers/product.go +++ b/backend/gestion/handlers/product.go @@ -914,7 +914,7 @@ func ServeMedia(c *gin.Context) { return } - body, contentType, err := s3Service.GetFile(key) + body, contentType, err := s3Service.GetFile(c.Request.Context(), key) if err != nil { c.JSON(http.StatusNotFound, gin.H{"error": "Média non trouvé"}) return diff --git a/backend/gestion/main.go b/backend/gestion/main.go index 4e010c73..bfc292f8 100644 --- a/backend/gestion/main.go +++ b/backend/gestion/main.go @@ -179,7 +179,7 @@ func main() { c.Next() }) - //r.Static("/uploads", "./uploads") + r.Static("/uploads", "./uploads") routes.SetupRoutes(r, database, geoService, s3Service) diff --git a/backend/gestion/services/s3.go b/backend/gestion/services/s3.go index bcf31e3c..fd3583ea 100644 --- a/backend/gestion/services/s3.go +++ b/backend/gestion/services/s3.go @@ -102,10 +102,8 @@ func (s *S3Service) UploadFileWithName(fileHeader *multipart.FileHeader, folder, } // GetFile récupère un objet depuis RustFS (stream + content-type) pour le proxy. -func (s *S3Service) GetFile(key string) (io.ReadCloser, string, error) { - ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) - defer cancel() - +// Le contexte doit rester actif pendant toute la lecture du body par l'appelant. +func (s *S3Service) GetFile(ctx context.Context, key string) (io.ReadCloser, string, error) { out, err := s.client.GetObject(ctx, &s3.GetObjectInput{ Bucket: aws.String(s.bucketName), Key: aws.String(key),