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

@@ -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<PushService> _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<PushService> 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;

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;
namespace DysonNetwork.Ring.Startup;
@@ -15,6 +15,15 @@ public static class ScheduledJobsConfiguration
.ForJob(appDatabaseRecyclingJob)
.WithIdentity("AppDatabaseRecyclingTrigger")
.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);

View File

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