From e49a1ec49a44c82c3f7af6593b79012fe0c90264 Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Wed, 3 Dec 2025 21:42:18 +0800 Subject: [PATCH] :sparkles: Push token clean up when invalid --- .../Handlers/ActionLogFlushHandler.cs | 4 +-- DysonNetwork.Ring/Notification/PushService.cs | 21 ++++++++--- .../Services/PushSubFlushHandler.cs | 35 +++++++++++++++++++ .../Startup/ScheduledJobsConfiguration.cs | 11 +++++- .../Startup/ServiceCollectionExtensions.cs | 1 + 5 files changed, 64 insertions(+), 8 deletions(-) create mode 100644 DysonNetwork.Ring/Services/PushSubFlushHandler.cs diff --git a/DysonNetwork.Pass/Handlers/ActionLogFlushHandler.cs b/DysonNetwork.Pass/Handlers/ActionLogFlushHandler.cs index 5833b69..29218d2 100644 --- a/DysonNetwork.Pass/Handlers/ActionLogFlushHandler.cs +++ b/DysonNetwork.Pass/Handlers/ActionLogFlushHandler.cs @@ -6,11 +6,11 @@ using Quartz; namespace DysonNetwork.Pass.Handlers; -public class ActionLogFlushHandler(IServiceProvider serviceProvider) : IFlushHandler +public class ActionLogFlushHandler(IServiceProvider sp) : IFlushHandler { public async Task FlushAsync(IReadOnlyList items) { - using var scope = serviceProvider.CreateScope(); + using var scope = sp.CreateScope(); var db = scope.ServiceProvider.GetRequiredService(); var now = SystemClock.Instance.GetCurrentInstant(); diff --git a/DysonNetwork.Ring/Notification/PushService.cs b/DysonNetwork.Ring/Notification/PushService.cs index 94fa189..8c78d16 100644 --- a/DysonNetwork.Ring/Notification/PushService.cs +++ b/DysonNetwork.Ring/Notification/PushService.cs @@ -2,6 +2,7 @@ using CorePush.Apple; using CorePush.Firebase; using DysonNetwork.Ring.Connection; using DysonNetwork.Ring.Services; +using DysonNetwork.Shared.Cache; using DysonNetwork.Shared.Models; using DysonNetwork.Shared.Proto; using Microsoft.EntityFrameworkCore; @@ -17,12 +18,14 @@ public class PushService private readonly ILogger _logger; private readonly FirebaseSender? _fcm; private readonly ApnSender? _apns; + private readonly FlushBufferService _fbs; private readonly string? _apnsTopic; public PushService( IConfiguration config, AppDatabase db, QueueService queueService, + FlushBufferService fbs, IHttpClientFactory httpFactory, ILogger logger ) @@ -52,6 +55,7 @@ public class PushService } _db = db; + _fbs = fbs; _queueService = queueService; _logger = logger; } @@ -144,14 +148,15 @@ public class PushService _ = _queueService.EnqueuePushNotification(notification, Guid.Parse(accountId), save); } - public async Task DeliverPushNotification(SnNotification notification, CancellationToken cancellationToken = default) + public async Task DeliverPushNotification(SnNotification notification, + CancellationToken cancellationToken = default) { WebSocketService.SendPacketToAccount(notification.AccountId, new WebSocketPacket() { Type = "notifications.new", Data = notification, }); - + try { _logger.LogInformation( @@ -260,7 +265,8 @@ public class PushService await DeliverPushNotification(notification); } - private async Task SendPushNotificationAsync(SnNotificationPushSubscription subscription, SnNotification notification) + private async Task SendPushNotificationAsync(SnNotificationPushSubscription subscription, + SnNotification notification) { try { @@ -302,7 +308,9 @@ public class PushService } }); - if (fcmResult.Error != null) + if (fcmResult.StatusCode is 404 or 410) + _fbs.Enqueue(new PushSubRemovalRequest { SubId = subscription.Id }); + else if (fcmResult.Error != null) throw new Exception($"Notification pushed failed ({fcmResult.StatusCode}) {fcmResult.Error}"); break; @@ -338,7 +346,10 @@ public class PushService apnsPriority: notification.Priority, apnPushType: ApnPushType.Alert ); - if (apnResult.Error != null) + + if (apnResult.StatusCode is 404 or 410) + _fbs.Enqueue(new PushSubRemovalRequest { SubId = subscription.Id }); + else if (apnResult.Error != null) throw new Exception($"Notification pushed failed ({apnResult.StatusCode}) {apnResult.Error}"); break; diff --git a/DysonNetwork.Ring/Services/PushSubFlushHandler.cs b/DysonNetwork.Ring/Services/PushSubFlushHandler.cs new file mode 100644 index 0000000..ed2b98e --- /dev/null +++ b/DysonNetwork.Ring/Services/PushSubFlushHandler.cs @@ -0,0 +1,35 @@ +using DysonNetwork.Shared.Cache; +using Microsoft.EntityFrameworkCore; +using Quartz; + +namespace DysonNetwork.Ring.Services; + +public class PushSubRemovalRequest +{ + public Guid SubId { get; set; } +} + +public class PushSubFlushHandler(IServiceProvider sp) : IFlushHandler +{ + public async Task FlushAsync(IReadOnlyList items) + { + using var scope = sp.CreateScope(); + var db = scope.ServiceProvider.GetRequiredService(); + var logger = scope.ServiceProvider.GetRequiredService>(); + + var tokenIds = items.Select(x => x.SubId).Distinct().ToList(); + + var count = await db.PushSubscriptions + .Where(s => tokenIds.Contains(s.Id)) + .ExecuteDeleteAsync(); + logger.LogInformation("Removed {Count} invalid push notification tokens...", count); + } +} + +public class PushSubFlushJob(FlushBufferService fbs, PushSubFlushHandler hdl) : IJob +{ + public async Task Execute(IJobExecutionContext context) + { + await fbs.FlushAsync(hdl); + } +} diff --git a/DysonNetwork.Ring/Startup/ScheduledJobsConfiguration.cs b/DysonNetwork.Ring/Startup/ScheduledJobsConfiguration.cs index bdc461a..31af764 100644 --- a/DysonNetwork.Ring/Startup/ScheduledJobsConfiguration.cs +++ b/DysonNetwork.Ring/Startup/ScheduledJobsConfiguration.cs @@ -1,4 +1,4 @@ -using DysonNetwork.Ring.Notification; +using DysonNetwork.Ring.Services; using Quartz; namespace DysonNetwork.Ring.Startup; @@ -15,6 +15,15 @@ public static class ScheduledJobsConfiguration .ForJob(appDatabaseRecyclingJob) .WithIdentity("AppDatabaseRecyclingTrigger") .WithCronSchedule("0 0 0 * * ?")); + + q.AddJob(opts => opts.WithIdentity("PushSubFlush")); + q.AddTrigger(opts => opts + .ForJob("PushSubFlush") + .WithIdentity("PushSubFlushTrigger") + .WithSimpleSchedule(o => o + .WithIntervalInMinutes(5) + .RepeatForever()) + ); }); services.AddQuartzHostedService(q => q.WaitForJobsToComplete = true); diff --git a/DysonNetwork.Ring/Startup/ServiceCollectionExtensions.cs b/DysonNetwork.Ring/Startup/ServiceCollectionExtensions.cs index 093359f..c5b2f4b 100644 --- a/DysonNetwork.Ring/Startup/ServiceCollectionExtensions.cs +++ b/DysonNetwork.Ring/Startup/ServiceCollectionExtensions.cs @@ -55,6 +55,7 @@ public static class ServiceCollectionExtensions public static IServiceCollection AddAppFlushHandlers(this IServiceCollection services) { services.AddSingleton(); + services.AddScoped(); return services; }