package utils import ( "errors" "fmt" "log" "os" "path/filepath" "strings" "gorm.io/gorm" "canguidev/shelfy/internal/models" ) func CreateDefaultFolder(db *gorm.DB) { // noms “logiques” attendus en base targets := []string{"Film", "Série", "Manga", "Magazine"} // 0) Nettoyage global : si des lignes ont path "upload/XXX", on les normalise en "XXX" if err := normalizeExistingPaths(db); err != nil { log.Printf("[DB] Warning: normalisation initiale partielle: %v", err) } for _, name := range targets { diskPath := filepath.Join("upload", name) // répertoire physique // 1) Toujours créer le dossier physique if err := os.MkdirAll(diskPath, 0o755); err != nil { log.Printf("[FOLDER] Erreur création '%s' : %v", diskPath, err) continue } // 2) En base : on veut UNE ligne unique avec Path=Name et PathName=Name // On cherche tout ce qui pourrait correspondre à ce nom (avec ou sans "upload/") var rows []models.PathDownload if err := db.Where("path = ? OR path = ? OR path_name = ? OR path_name = ?", name, "upload/"+name, name, "upload/"+name, ).Find(&rows).Error; err != nil { log.Printf("[DB] Lookup '%s' échouée : %v", name, err) continue } switch len(rows) { case 0: // 2a) Rien trouvé → créer la ligne normalisée row := models.PathDownload{Path: name, PathName: name} if err := db.Create(&row).Error; err != nil { log.Printf("[DB] Création PathDownload('%s') KO : %v", name, err) } else { log.Printf("[DB] Créé PathDownload id=%v (%s)", row.ID, name) } default: // 2b) On a des lignes → on les normalise et on garde une seule // 1) mettre à jour la première ligne en Path=Name, PathName=Name main := rows[0] needUpdate := (main.Path != name) || (main.PathName != name) if needUpdate { if err := db.Model(&main).Updates(map[string]any{ "path": name, "path_name": name, }).Error; err != nil { log.Printf("[DB] Update id=%v -> (%s|%s) KO : %v", main.ID, name, name, err) } else { log.Printf("[DB] Normalisé id=%v -> (%s|%s)", main.ID, name, name) } } // 2) supprimer les doublons résiduels (même nom logique) if len(rows) > 1 { var dupIDs []uint for _, r := range rows[1:] { dupIDs = append(dupIDs, r.ID) } if err := db.Where("id IN ?", dupIDs).Delete(&models.PathDownload{}).Error; err != nil { log.Printf("[DB] Suppression doublons (%v) KO : %v", dupIDs, err) } else { log.Printf("[DB] Doublons supprimés pour '%s' : %v", name, dupIDs) } } } log.Printf("[FOLDER] OK : %s (DB='%s')", diskPath, name) } } // normalizeExistingPaths met à jour toutes les lignes dont path commence par "upload/" // en supprimant ce préfixe, et aligne path_name si nécessaire. Elle supprime aussi // d’éventuels doublons créés par cette normalisation. func normalizeExistingPaths(db *gorm.DB) error { // 1) Récupérer les lignes impactées var rows []models.PathDownload if err := db.Where("path LIKE ?", "upload/%").Find(&rows).Error; err != nil { return err } for _, r := range rows { trimmed := strings.TrimPrefix(r.Path, "upload/") updates := map[string]any{"path": trimmed} if r.PathName == "" || strings.HasPrefix(r.PathName, "upload/") { updates["path_name"] = trimmed } if err := db.Model(&r).Updates(updates).Error; err != nil { return fmt.Errorf("update id=%v (%s -> %s) : %w", r.ID, r.Path, trimmed, err) } } // 2) Éliminer d’éventuels doublons (même path normalisé), on garde le plus petit id type rec struct { ID uint Path string } var all []rec if err := db.Model(&models.PathDownload{}).Select("id, path").Order("path, id").Scan(&all).Error; err != nil { return err } seen := make(map[string]uint) var dupIDs []uint for _, r := range all { if keep, ok := seen[r.Path]; ok { // doublon : on marque pour suppression dupIDs = append(dupIDs, r.ID) _ = keep } else { seen[r.Path] = r.ID } } if len(dupIDs) > 0 { if err := db.Where("id IN ?", dupIDs).Delete(&models.PathDownload{}).Error; err != nil && !errors.Is(err, gorm.ErrRecordNotFound) { return fmt.Errorf("delete dups %v : %w", dupIDs, err) } log.Printf("[DB] Doublons supprimés après normalisation: %v", dupIDs) } return nil }