[backend/masto-client] Catch GracefulExceptions correctly in push handler

This commit is contained in:
Laura Hausmann 2024-03-27 17:56:07 +01:00
parent fa75409923
commit d4e8d7a6f6
No known key found for this signature in database
GPG key ID: D044E84C5BE01605

View file

@ -38,9 +38,10 @@ public class PushService(
await using var scope = scopeFactory.CreateAsyncScope(); await using var scope = scopeFactory.CreateAsyncScope();
await using var db = scope.ServiceProvider.GetRequiredService<DatabaseContext>(); await using var db = scope.ServiceProvider.GetRequiredService<DatabaseContext>();
var type = NotificationEntity.EncodeType(notification.Type);
var subscriptions = await db.PushSubscriptions.Where(p => p.User == notification.Notifiee) var subscriptions = await db.PushSubscriptions.Where(p => p.User == notification.Notifiee)
.Include(pushSubscription => pushSubscription.OauthToken) .Include(pushSubscription => pushSubscription.OauthToken)
.Where(p => p.Types.Contains(NotificationEntity.EncodeType(notification.Type))) .Where(p => p.Types.Contains(type))
.ToListAsync(); .ToListAsync();
if (subscriptions.Count == 0) if (subscriptions.Count == 0)
@ -58,91 +59,88 @@ public class PushService(
await db.Followings.AnyAsync(p => p.Follower == notification.Notifier && await db.Followings.AnyAsync(p => p.Follower == notification.Notifier &&
p.Followee == notification.Notifiee); p.Followee == notification.Notifiee);
try var renderer = scope.ServiceProvider.GetRequiredService<NotificationRenderer>();
var rendered = await renderer.RenderAsync(notification, notification.Notifiee);
var name = rendered.Notifier.DisplayName;
var subject = rendered.Type switch
{ {
var renderer = scope.ServiceProvider.GetRequiredService<NotificationRenderer>(); "favourite" => $"{name} favorited your post",
var rendered = await renderer.RenderAsync(notification, notification.Notifiee); "follow" => $"{name} is now following you",
var name = rendered.Notifier.DisplayName; "follow_request" => $"Pending follower: {name}",
var subject = rendered.Type switch "mention" => $"You were mentioned by {name}",
"poll" => $"A poll by {name} has ended",
"reblog" => $"{name} boosted your post",
"status" => $"{name} just posted",
"update" => $"{name} edited a post",
_ => $"New notification from {name}"
};
var body = "";
if (notification.Note != null)
body = notification.Note.Cw ?? notification.Note.Text ?? "";
body = body.Trim().Truncate(140).TrimEnd();
if (body.Length > 137)
body = body.Truncate(137).TrimEnd() + "...";
var meta = scope.ServiceProvider.GetRequiredService<MetaService>();
var (priv, pub) = await meta.GetMany(MetaEntity.VapidPrivateKey, MetaEntity.VapidPublicKey);
var client = new WebPushClient(httpClient);
client.SetVapidDetails(new VapidDetails($"https://{config.Value.WebDomain}", pub, priv));
var matchingSubscriptions =
from subscription in subscriptions
where subscription.Policy != PushSubscription.PushPolicy.Followed || followed
where subscription.Policy != PushSubscription.PushPolicy.Follower || follower
where subscription.Policy != PushSubscription.PushPolicy.None || isSelf
select subscription;
foreach (var subscription in matchingSubscriptions)
{
try
{ {
"favourite" => $"{name} favorited your post", var res = new PushSchemas.PushNotification
"follow" => $"{name} is now following you", {
"follow_request" => $"Pending follower: {name}", AccessToken = subscription.OauthToken.Token,
"mention" => $"You were mentioned by {name}", NotificationType = rendered.Type,
"poll" => $"A poll by {name} has ended", NotificationId = long.Parse(rendered.Id),
"reblog" => $"{name} boosted your post", IconUrl = rendered.Notifier.AvatarUrl,
"status" => $"{name} just posted", Title = subject,
"update" => $"{name} edited a post", Body = body
_ => $"New notification from {name}" };
};
var body = ""; var sub = new WebPushSubscription
{
Endpoint = subscription.Endpoint,
P256DH = subscription.PublicKey,
Auth = subscription.AuthSecret,
PushMode = PushMode.AesGcm
};
if (notification.Note != null) await client.SendNotificationAsync(sub, JsonSerializer.Serialize(res));
body = notification.Note.Cw ?? notification.Note.Text ?? ""; }
catch (Exception e)
body = body.Trim().Truncate(140).TrimEnd();
if (body.Length > 137)
body = body.Truncate(137).TrimEnd() + "...";
var meta = scope.ServiceProvider.GetRequiredService<MetaService>();
var (priv, pub) = await meta.GetMany(MetaEntity.VapidPrivateKey, MetaEntity.VapidPublicKey);
var client = new WebPushClient(httpClient);
client.SetVapidDetails(new VapidDetails($"https://{config.Value.WebDomain}", pub, priv));
var matchingSubscriptions =
from subscription in subscriptions
where subscription.Policy != PushSubscription.PushPolicy.Followed || followed
where subscription.Policy != PushSubscription.PushPolicy.Follower || follower
where subscription.Policy != PushSubscription.PushPolicy.None || isSelf
select subscription;
foreach (var subscription in matchingSubscriptions)
{ {
try switch (e)
{ {
var res = new PushSchemas.PushNotification case WebPushException { StatusCode: HttpStatusCode.Gone }:
{ await db.PushSubscriptions.Where(p => p.Id == subscription.Id).ExecuteDeleteAsync();
AccessToken = subscription.OauthToken.Token, break;
NotificationType = rendered.Type, case WebPushException we:
NotificationId = long.Parse(rendered.Id), logger.LogDebug("Push notification delivery failed: {e}", we.Message);
IconUrl = rendered.Notifier.AvatarUrl, break;
Title = subject, default:
Body = body logger.LogDebug("Push notification delivery threw exception: {e}", e);
}; break;
var sub = new WebPushSubscription
{
Endpoint = subscription.Endpoint,
P256DH = subscription.PublicKey,
Auth = subscription.AuthSecret,
PushMode = PushMode.AesGcm
};
await client.SendNotificationAsync(sub, JsonSerializer.Serialize(res));
}
catch (Exception e)
{
switch (e)
{
case WebPushException { StatusCode: HttpStatusCode.Gone }:
await db.PushSubscriptions.Where(p => p.Id == subscription.Id).ExecuteDeleteAsync();
break;
case WebPushException we:
logger.LogDebug("Push notification delivery failed: {e}", we.Message);
break;
default:
logger.LogDebug("Push notification delivery threw exception: {e}", e);
break;
}
} }
} }
} }
catch (GracefulException) }
{ catch (GracefulException)
// Unsupported notification type {
} // Unsupported notification type
} }
catch (Exception e) catch (Exception e)
{ {