diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..f3254c2 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,13 @@ +bin/ +obj/ +.git/ +.gitignore +*.md +README.md +tests/ +docs/ +.vs/ +.vscode/ +**/.DS_Store +**/Thumbs.db + diff --git a/src/BCards.Web/Models/UserPage.cs b/src/BCards.Web/Models/UserPage.cs index 2c4f02c..5ef168c 100644 --- a/src/BCards.Web/Models/UserPage.cs +++ b/src/BCards.Web/Models/UserPage.cs @@ -92,6 +92,16 @@ public class UserPage : IPageDisplay [BsonElement("previewViewCount")] public int PreviewViewCount { get; set; } = 0; + // Exclusão lógica + [BsonElement("deletedAt")] + public DateTime? DeletedAt { get; set; } + + [BsonElement("deletionReason")] + public string? DeletionReason { get; set; } // "trial_expired", "user_requested", "moderation_violation" + + [BsonIgnore] + public bool IsDeleted => DeletedAt.HasValue; + public string FullUrl => $"page/{Category}/{Slug}"; /// diff --git a/src/BCards.Web/Program.cs b/src/BCards.Web/Program.cs index b9c9bf4..f8e58a5 100644 --- a/src/BCards.Web/Program.cs +++ b/src/BCards.Web/Program.cs @@ -564,7 +564,6 @@ app.UseAuthorization(); app.UseMiddleware(); app.UseMiddleware(); app.UseMiddleware(); -app.UseMiddleware(); app.UseMiddleware(); app.UseMiddleware(); diff --git a/src/BCards.Web/Services/ModerationService.cs b/src/BCards.Web/Services/ModerationService.cs index 7246bfa..754fc2b 100644 --- a/src/BCards.Web/Services/ModerationService.cs +++ b/src/BCards.Web/Services/ModerationService.cs @@ -27,7 +27,7 @@ public class ModerationService : IModerationService public async Task GeneratePreviewTokenAsync(string pageId) { var token = Guid.NewGuid().ToString("N")[..16]; - var expiry = DateTime.UtcNow.AddDays(30); // Token válido por 30 dias + var expiry = DateTime.UtcNow.AddHours(4); // Token válido por 4 horas var page = await _userPageRepository.GetByIdAsync(pageId); page.PreviewToken = token; page.PreviewTokenExpiry = expiry; diff --git a/src/BCards.Web/Services/TrialExpirationService.cs b/src/BCards.Web/Services/TrialExpirationService.cs index d2f5f9f..c020a31 100644 --- a/src/BCards.Web/Services/TrialExpirationService.cs +++ b/src/BCards.Web/Services/TrialExpirationService.cs @@ -1,5 +1,6 @@ using BCards.Web.Models; using BCards.Web.Repositories; +using BCards.Web.ViewModels; using MongoDB.Driver; namespace BCards.Web.Services; @@ -75,7 +76,7 @@ public class TrialExpirationService : BackgroundService _logger.LogInformation("Checking for expired trials..."); - // Get all active trial subscriptions + // Process trial expirations var trialSubscriptions = await subscriptionRepository.GetTrialSubscriptionsAsync(); var now = DateTime.UtcNow; @@ -118,6 +119,9 @@ public class TrialExpirationService : BackgroundService } _logger.LogInformation("Finished checking trial expirations"); + + // Process permanent deletions (pages deleted for more than 30 days) + await ProcessPermanentDeletionsAsync(userPageRepository); } catch (Exception ex) { @@ -127,15 +131,17 @@ public class TrialExpirationService : BackgroundService } private async Task HandleTrialExpiredAsync( - User user, - Subscription subscription, + User user, + Subscription subscription, IUserPageRepository userPageRepository) { - // Deactivate user page + // Mark user page as expired (logical deletion) var userPage = await userPageRepository.GetByUserIdAsync(user.Id); if (userPage != null) { - userPage.IsActive = false; + userPage.Status = PageStatus.Expired; + userPage.DeletedAt = DateTime.UtcNow; + userPage.DeletionReason = "trial_expired"; userPage.UpdatedAt = DateTime.UtcNow; await userPageRepository.UpdateAsync(userPage); } @@ -218,4 +224,48 @@ public class TrialExpirationService : BackgroundService // TODO: Get from configuration return "https://bcards.com.br/pricing"; } + + private async Task ProcessPermanentDeletionsAsync(IUserPageRepository userPageRepository) + { + try + { + _logger.LogInformation("Checking for pages to permanently delete..."); + + // Find pages that have been logically deleted for more than 30 days + var cutoffDate = DateTime.UtcNow.AddDays(-30); + + // Get all expired pages older than 30 days + var filter = MongoDB.Driver.Builders.Filter.And( + MongoDB.Driver.Builders.Filter.Eq(p => p.Status, PageStatus.Expired), + MongoDB.Driver.Builders.Filter.Ne(p => p.DeletedAt, null), + MongoDB.Driver.Builders.Filter.Lt(p => p.DeletedAt, cutoffDate) + ); + + var pagesToDelete = await userPageRepository.GetManyAsync(filter); + + _logger.LogInformation($"Found {pagesToDelete.Count} pages to permanently delete"); + + foreach (var page in pagesToDelete) + { + try + { + _logger.LogInformation($"Permanently deleting page {page.Id} ({page.DisplayName}) - deleted at {page.DeletedAt}"); + await userPageRepository.DeleteAsync(page.Id); + } + catch (Exception ex) + { + _logger.LogError(ex, $"Error permanently deleting page {page.Id}"); + } + } + + if (pagesToDelete.Count > 0) + { + _logger.LogInformation($"Permanently deleted {pagesToDelete.Count} pages"); + } + } + catch (Exception ex) + { + _logger.LogError(ex, "Error processing permanent deletions"); + } + } } \ No newline at end of file diff --git a/src/BCards.Web/Views/Admin/Dashboard.cshtml b/src/BCards.Web/Views/Admin/Dashboard.cshtml index 4a8f367..2d8c8b3 100644 --- a/src/BCards.Web/Views/Admin/Dashboard.cshtml +++ b/src/BCards.Web/Views/Admin/Dashboard.cshtml @@ -74,6 +74,9 @@ case BCards.Web.ViewModels.PageStatus.Rejected: Rejeitada break; + case BCards.Web.ViewModels.PageStatus.Expired: + Trial Expirado + break; case BCards.Web.ViewModels.PageStatus.Creating: Em Criação break; @@ -83,6 +86,20 @@ } + @* Exibir histórico de rejeição se existir *@ + @if (pageItem.Status == BCards.Web.ViewModels.PageStatus.Rejected && !string.IsNullOrEmpty(pageItem.Motive)) + { +
+
+ +
+ Motivo da rejeição:
+ @pageItem.Motive +
+
+
+ } + @if (Model.CurrentPlan.AllowsAnalytics) {
@@ -129,6 +146,15 @@ Aguardando Moderação } + else if (pageItem.Status == BCards.Web.ViewModels.PageStatus.Expired) + { + + Reativar Página + + + Sua página foi pausada. Escolha um plano para reativá-la. + + } else {