using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Microsoft.Extensions.DependencyInjection; using Microsoft.EntityFrameworkCore; using MotoresArgentinosV2.Infrastructure.Data; namespace MotoresArgentinosV2.Infrastructure.Services; public class TokenCleanupService : BackgroundService { private readonly IServiceProvider _serviceProvider; private readonly ILogger _logger; private readonly TimeSpan _cleanupInterval = TimeSpan.FromHours(24); // Ejecutar cada 24hs public TokenCleanupService(IServiceProvider serviceProvider, ILogger logger) { _serviceProvider = serviceProvider; _logger = logger; } protected override async Task ExecuteAsync(CancellationToken stoppingToken) { _logger.LogInformation("Servicio de Limpieza de Tokens iniciado."); while (!stoppingToken.IsCancellationRequested) { try { await CleanExpiredTokensAsync(stoppingToken); // Esperar hasta la próxima ejecución await Task.Delay(_cleanupInterval, stoppingToken); } catch (OperationCanceledException) { // El servicio se está deteniendo, es normal break; } catch (Exception ex) { _logger.LogError(ex, "Error durante la limpieza de tokens."); // Si hay un error, esperamos un poco antes de reintentar (evitar bucle infinito de errores) await Task.Delay(TimeSpan.FromMinutes(5), stoppingToken); } } } private async Task CleanExpiredTokensAsync(CancellationToken stoppingToken) { using (var scope = _serviceProvider.CreateScope()) { var context = scope.ServiceProvider.GetRequiredService(); // Definir criterios de limpieza var cutoffDate = DateTime.UtcNow; // Tokens ya expirados var revokedCutoff = DateTime.UtcNow.AddDays(-30); // Tokens revocados hace más de 30 días (auditoría) _logger.LogInformation("Ejecutando limpieza de RefreshTokens expirados o antiguos..."); // Opción 1: Borrado con SQL Raw para eficiencia en lotes grandes // Asumimos que la tabla se llama 'RefreshTokens' en la DB var rowsAffected = await context.Database.ExecuteSqlRawAsync( "DELETE FROM [RefreshTokens] WHERE [Expires] < @cutoffDate OR ([Revoked] IS NOT NULL AND [Revoked] < @revokedCutoff)", new[] { new Microsoft.Data.SqlClient.SqlParameter("@cutoffDate", cutoffDate), new Microsoft.Data.SqlClient.SqlParameter("@revokedCutoff", revokedCutoff) }, stoppingToken); _logger.LogInformation("Limpieza completada. {Count} tokens eliminados.", rowsAffected); } } }