Push token clean up when invalid

This commit is contained in:
2025-12-03 21:42:18 +08:00
parent a88f42b26a
commit e49a1ec49a
5 changed files with 64 additions and 8 deletions

View File

@@ -6,11 +6,11 @@ using Quartz;
namespace DysonNetwork.Pass.Handlers; namespace DysonNetwork.Pass.Handlers;
public class ActionLogFlushHandler(IServiceProvider serviceProvider) : IFlushHandler<SnActionLog> public class ActionLogFlushHandler(IServiceProvider sp) : IFlushHandler<SnActionLog>
{ {
public async Task FlushAsync(IReadOnlyList<SnActionLog> items) public async Task FlushAsync(IReadOnlyList<SnActionLog> items)
{ {
using var scope = serviceProvider.CreateScope(); using var scope = sp.CreateScope();
var db = scope.ServiceProvider.GetRequiredService<AppDatabase>(); var db = scope.ServiceProvider.GetRequiredService<AppDatabase>();
var now = SystemClock.Instance.GetCurrentInstant(); var now = SystemClock.Instance.GetCurrentInstant();

View File

@@ -2,6 +2,7 @@ using CorePush.Apple;
using CorePush.Firebase; using CorePush.Firebase;
using DysonNetwork.Ring.Connection; using DysonNetwork.Ring.Connection;
using DysonNetwork.Ring.Services; using DysonNetwork.Ring.Services;
using DysonNetwork.Shared.Cache;
using DysonNetwork.Shared.Models; using DysonNetwork.Shared.Models;
using DysonNetwork.Shared.Proto; using DysonNetwork.Shared.Proto;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
@@ -17,12 +18,14 @@ public class PushService
private readonly ILogger<PushService> _logger; private readonly ILogger<PushService> _logger;
private readonly FirebaseSender? _fcm; private readonly FirebaseSender? _fcm;
private readonly ApnSender? _apns; private readonly ApnSender? _apns;
private readonly FlushBufferService _fbs;
private readonly string? _apnsTopic; private readonly string? _apnsTopic;
public PushService( public PushService(
IConfiguration config, IConfiguration config,
AppDatabase db, AppDatabase db,
QueueService queueService, QueueService queueService,
FlushBufferService fbs,
IHttpClientFactory httpFactory, IHttpClientFactory httpFactory,
ILogger<PushService> logger ILogger<PushService> logger
) )
@@ -52,6 +55,7 @@ public class PushService
} }
_db = db; _db = db;
_fbs = fbs;
_queueService = queueService; _queueService = queueService;
_logger = logger; _logger = logger;
} }
@@ -144,7 +148,8 @@ public class PushService
_ = _queueService.EnqueuePushNotification(notification, Guid.Parse(accountId), save); _ = _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() WebSocketService.SendPacketToAccount(notification.AccountId, new WebSocketPacket()
{ {
@@ -260,7 +265,8 @@ public class PushService
await DeliverPushNotification(notification); await DeliverPushNotification(notification);
} }
private async Task SendPushNotificationAsync(SnNotificationPushSubscription subscription, SnNotification notification) private async Task SendPushNotificationAsync(SnNotificationPushSubscription subscription,
SnNotification notification)
{ {
try 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}"); throw new Exception($"Notification pushed failed ({fcmResult.StatusCode}) {fcmResult.Error}");
break; break;
@@ -338,7 +346,10 @@ public class PushService
apnsPriority: notification.Priority, apnsPriority: notification.Priority,
apnPushType: ApnPushType.Alert 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}"); throw new Exception($"Notification pushed failed ({apnResult.StatusCode}) {apnResult.Error}");
break; break;

View File

@@ -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<PushSubRemovalRequest>
{
public async Task FlushAsync(IReadOnlyList<PushSubRemovalRequest> items)
{
using var scope = sp.CreateScope();
var db = scope.ServiceProvider.GetRequiredService<AppDatabase>();
var logger = scope.ServiceProvider.GetRequiredService<ILogger<PushSubFlushHandler>>();
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);
}
}

View File

@@ -1,4 +1,4 @@
using DysonNetwork.Ring.Notification; using DysonNetwork.Ring.Services;
using Quartz; using Quartz;
namespace DysonNetwork.Ring.Startup; namespace DysonNetwork.Ring.Startup;
@@ -15,6 +15,15 @@ public static class ScheduledJobsConfiguration
.ForJob(appDatabaseRecyclingJob) .ForJob(appDatabaseRecyclingJob)
.WithIdentity("AppDatabaseRecyclingTrigger") .WithIdentity("AppDatabaseRecyclingTrigger")
.WithCronSchedule("0 0 0 * * ?")); .WithCronSchedule("0 0 0 * * ?"));
q.AddJob<PushSubFlushJob>(opts => opts.WithIdentity("PushSubFlush"));
q.AddTrigger(opts => opts
.ForJob("PushSubFlush")
.WithIdentity("PushSubFlushTrigger")
.WithSimpleSchedule(o => o
.WithIntervalInMinutes(5)
.RepeatForever())
);
}); });
services.AddQuartzHostedService(q => q.WaitForJobsToComplete = true); services.AddQuartzHostedService(q => q.WaitForJobsToComplete = true);

View File

@@ -55,6 +55,7 @@ public static class ServiceCollectionExtensions
public static IServiceCollection AddAppFlushHandlers(this IServiceCollection services) public static IServiceCollection AddAppFlushHandlers(this IServiceCollection services)
{ {
services.AddSingleton<FlushBufferService>(); services.AddSingleton<FlushBufferService>();
services.AddScoped<PushSubFlushHandler>();
return services; return services;
} }