package cleaner import ( "log" "os" "path/filepath" "time" "app_compartir/internal/db" ) type Cleaner struct { db *db.Database uploadDir string interval time.Duration stopChan chan struct{} } func New(database *db.Database, uploadDir string, interval time.Duration) *Cleaner { if interval <= 0 { interval = 10 * time.Minute } return &Cleaner{ db: database, uploadDir: uploadDir, interval: interval, stopChan: make(chan struct{}), } } func (c *Cleaner) Start() { log.Printf("[CLEANER] Iniciando servicio de purga automática de archivos Guest (intervalo: %v)", c.interval) // Ejecutar una vez al inicio c.runPurge() go func() { ticker := time.NewTicker(c.interval) defer ticker.Stop() for { select { case <-ticker.C: c.runPurge() case <-c.stopChan: log.Println("[CLEANER] Servicio de purga detenido.") return } } }() } func (c *Cleaner) Stop() { close(c.stopChan) } func (c *Cleaner) runPurge() { expiredShares, err := c.db.GetExpiredGuestShares() if err != nil { log.Printf("[CLEANER ERROR] Error consultando archivos de invitados expirados: %v", err) return } for _, s := range expiredShares { // Eliminar archivo físico si existe if s.StoredPath != "" { fullPath := filepath.Join(c.uploadDir, s.StoredPath) if err := os.Remove(fullPath); err != nil && !os.IsNotExist(err) { log.Printf("[CLEANER ADVERTENCIA] No se pudo borrar archivo físico %s: %v", fullPath, err) } } // Eliminar registro de BD (solo afecta a registros con is_guest = 1) if err := c.db.PurgeShare(s.ID); err != nil { log.Printf("[CLEANER ERROR] No se pudo purgar registro de share %s: %v", s.ID, err) } else { log.Printf("[CLEANER] ✅ Archivo de invitado expirado (24h) purgado con éxito: ID=%s", s.ID) } } // Limpiar sesiones caducadas if err := c.db.PurgeExpiredSessions(); err != nil { log.Printf("[CLEANER ERROR] Error purgando sesiones expiradas: %v", err) } }