shelfy-v2/internal/utils/lib.go
2025-08-18 21:45:27 +02:00

132 lines
3.9 KiB
Go

package utils
import (
"errors"
"fmt"
"log"
"os"
"path/filepath"
"strings"
"gorm.io/gorm"
"canguidev/shelfy/internal/models"
)
// Synchronise le disque avec la base : Film, Série, Manga, Magazine.
// - Disque : crée "upload/<Nom>" si manquant
// - DB : 1 ligne unique par nom (Path="<Nom>", PathName="<Nom>")
func CreateDefaultFolder(db *gorm.DB) {
targets := []string{"Film", "Série", "Manga", "Magazine"}
// 0) Normalise les entrées existantes (upload/X -> X) et dédoublonne
if err := normalizeExistingPaths(db); err != nil {
log.Printf("[DB] Avertissement: normalisation partielle: %v", err)
}
for _, name := range targets {
// 1) Dossier physique
diskPath := filepath.Join("upload", name)
if err := os.MkdirAll(diskPath, 0o755); err != nil {
log.Printf("[FOLDER] Erreur création '%s' : %v", diskPath, err)
continue
}
// 2) DB : cherche toute ligne liée à ce nom (avec/ 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:
// crée proprement
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:
// garde la première, normalise, supprime doublons
main := rows[0]
if main.Path != name || main.PathName != name {
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)
}
}
if len(rows) > 1 {
var dupIDs []int64
for _, r := range rows[1:] {
dupIDs = append(dupIDs, r.ID) // ID en int64
}
if err := db.Where("id IN ?", dupIDs).Delete(&models.PathDownload{}).Error; err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
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)
}
}
// Met à jour toutes les lignes path="upload/..." -> path="...",
// aligne path_name si nécessaire, puis supprime les doublons.
func normalizeExistingPaths(db *gorm.DB) error {
// 1) Normalisation des paths
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) Déduplication par path (on garde le plus petit id)
type rec struct {
ID int64 // <-- int64
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]int64)
var dupIDs []int64
for _, r := range all {
if _, ok := seen[r.Path]; ok {
dupIDs = append(dupIDs, r.ID)
} 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
}