chore: fix latest build
Backend - Build & Lint / build (push) Has been cancelled

This commit is contained in:
2026-06-29 18:56:00 +02:00
parent 9fcc77307f
commit 781212f706
5 changed files with 24 additions and 15 deletions
+6
View File
@@ -294,6 +294,11 @@ func InitDB() *Database {
log.Fatalf("❌ Erreur migration contacts: %v", err) 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 // Lancer le nettoyage périodique des tokens expirés
go database.cleanExpiredTokensPeriodically() go database.cleanExpiredTokensPeriodically()
@@ -399,6 +404,7 @@ func (db *Database) createTables() error {
product_id INTEGER NOT NULL REFERENCES products(id) ON DELETE CASCADE, product_id INTEGER NOT NULL REFERENCES products(id) ON DELETE CASCADE,
url TEXT NOT NULL, url TEXT NOT NULL,
type VARCHAR(50) NOT NULL, type VARCHAR(50) NOT NULL,
key TEXT NOT NULL DEFAULT '',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);`, );`,
+14 -9
View File
@@ -59,8 +59,8 @@ func validateMediaURL(url string) error {
if strings.Contains(url, "..") || strings.Contains(url, "...") || strings.Contains(url, "..//") { if strings.Contains(url, "..") || strings.Contains(url, "...") || strings.Contains(url, "..//") {
return fmt.Errorf("path traversal détecté dans l'URL") return fmt.Errorf("path traversal détecté dans l'URL")
} }
if !strings.HasPrefix(url, "/uploads/") { if !strings.HasPrefix(url, "/uploads/") && !strings.HasPrefix(url, "/media/") {
return fmt.Errorf("URL doit commencer par /uploads/") return fmt.Errorf("URL doit commencer par /uploads/ ou /media/")
} }
dangerousChars := []string{"<", ">", "\"", "'", ";", "|", "&", "$", "`", "\\"} dangerousChars := []string{"<", ">", "\"", "'", ";", "|", "&", "$", "`", "\\"}
for _, char := range dangerousChars { for _, char := range dangerousChars {
@@ -93,6 +93,11 @@ func (d *Database) CreateMedia(media any) error {
mediaType := m.GetType() mediaType := m.GetType()
mediaURL := m.GetURL() mediaURL := m.GetURL()
mediaKey := ""
if mediaPtr, isPtr := media.(*models.Media); isPtr {
mediaKey = mediaPtr.Key
}
if err := validateProductID(productID); err != nil { if err := validateProductID(productID); err != nil {
log.Printf("❌ [CreateMedia] %v", err) log.Printf("❌ [CreateMedia] %v", err)
return err return err
@@ -106,14 +111,14 @@ func (d *Database) CreateMedia(media any) error {
return err 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) log.Printf("❌ [InsertMedia] %v", err)
return err return err
} }
return nil 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 var exists bool
if err := d.GDB.Raw(`SELECT EXISTS(SELECT 1 FROM products WHERE id = ?)`, productID).Scan(&exists).Error; err != nil { 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) 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"` ID int `gorm:"column:id"`
} }
err := d.GDB.Raw(` err := d.GDB.Raw(`
INSERT INTO media (product_id, url, type, created_at) INSERT INTO media (product_id, url, type, key, created_at)
VALUES (?, ?, ?, ?) RETURNING id`, VALUES (?, ?, ?, ?, ?) RETURNING id`,
productID, mediaURL, mediaType, time.Now(), productID, mediaURL, mediaType, key, time.Now(),
).Scan(&result).Error ).Scan(&result).Error
if err != nil { if err != nil {
log.Printf("❌ [CreateMedia] Erreur INSERT: %v", err) log.Printf("❌ [CreateMedia] Erreur INSERT: %v", err)
@@ -154,7 +159,7 @@ func (d *Database) GetMediaByID(mediaID int) (*models.Media, error) {
var media models.Media var media models.Media
err := d.GDB.Raw(` 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 FROM media WHERE id = ?`, mediaID).Scan(&media).Error
if err != nil { if err != nil {
log.Printf("❌ [GetMediaByID] Erreur query: %v", err) log.Printf("❌ [GetMediaByID] Erreur query: %v", err)
@@ -179,7 +184,7 @@ func (d *Database) GetMediaByProductID(productID int) ([]models.Media, error) {
var mediaList []models.Media var mediaList []models.Media
err := d.GDB.Raw(` 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 = ? FROM media WHERE product_id = ?
ORDER BY id ASC`, productID).Scan(&mediaList).Error ORDER BY id ASC`, productID).Scan(&mediaList).Error
if err != nil { if err != nil {
+1 -1
View File
@@ -914,7 +914,7 @@ func ServeMedia(c *gin.Context) {
return return
} }
body, contentType, err := s3Service.GetFile(key) body, contentType, err := s3Service.GetFile(c.Request.Context(), key)
if err != nil { if err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "Média non trouvé"}) c.JSON(http.StatusNotFound, gin.H{"error": "Média non trouvé"})
return return
+1 -1
View File
@@ -179,7 +179,7 @@ func main() {
c.Next() c.Next()
}) })
//r.Static("/uploads", "./uploads") r.Static("/uploads", "./uploads")
routes.SetupRoutes(r, database, geoService, s3Service) routes.SetupRoutes(r, database, geoService, s3Service)
+2 -4
View File
@@ -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. // GetFile récupère un objet depuis RustFS (stream + content-type) pour le proxy.
func (s *S3Service) GetFile(key string) (io.ReadCloser, string, error) { // Le contexte doit rester actif pendant toute la lecture du body par l'appelant.
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) func (s *S3Service) GetFile(ctx context.Context, key string) (io.ReadCloser, string, error) {
defer cancel()
out, err := s.client.GetObject(ctx, &s3.GetObjectInput{ out, err := s.client.GetObject(ctx, &s3.GetObjectInput{
Bucket: aws.String(s.bucketName), Bucket: aws.String(s.bucketName),
Key: aws.String(key), Key: aws.String(key),