From 9c89c91ded2c0ff577c82463eed3c41ee766584a Mon Sep 17 00:00:00 2001 From: dakkar Date: Sat, 17 May 2025 12:59:52 +0100 Subject: [PATCH 1/2] handle "follow" notifs from deleted user the backend should not send these, but still, let's not explode --- .../frontend/src/components/MkNotification.vue | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/packages/frontend/src/components/MkNotification.vue b/packages/frontend/src/components/MkNotification.vue index 5b2a8dfc5a..220ca04d47 100644 --- a/packages/frontend/src/components/MkNotification.vue +++ b/packages/frontend/src/components/MkNotification.vue @@ -242,13 +242,18 @@ watch(props, async () => { const type = props.notification.type; // To avoid extra lookups, only do the query when it actually matters. - if (type === 'follow' || type === 'receiveFollowRequest') { - const user = await misskeyApi('users/show', { - userId: props.notification.userId, - }); + if ((type === 'follow' || type === 'receiveFollowRequest') && props.notification.userId) { + try { + const user = await misskeyApi('users/show', { + userId: props.notification.userId, + }); - userDetailed.value = user; - followRequestDone.value = !user.hasPendingFollowRequestToYou; + userDetailed.value = user; + followRequestDone.value = !user.hasPendingFollowRequestToYou; + } catch { + userDetailed.value = null; + followRequestDone.value = false; + } } else { userDetailed.value = null; followRequestDone.value = false; From 28aa62f98891ec66c57a0b043270f3612c82934d Mon Sep 17 00:00:00 2001 From: dakkar Date: Sun, 25 May 2025 12:42:59 +0100 Subject: [PATCH 2/2] handle `EntityNotFoundError` on all calls to `*.pack()` --- .../entities/NotificationEntityService.ts | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/packages/backend/src/core/entities/NotificationEntityService.ts b/packages/backend/src/core/entities/NotificationEntityService.ts index 77e6a1c7e7..cc8edfc666 100644 --- a/packages/backend/src/core/entities/NotificationEntityService.ts +++ b/packages/backend/src/core/entities/NotificationEntityService.ts @@ -23,6 +23,13 @@ import type { NoteEntityService } from './NoteEntityService.js'; const NOTE_REQUIRED_NOTIFICATION_TYPES = new Set(['note', 'mention', 'reply', 'renote', 'renote:grouped', 'quote', 'reaction', 'reaction:grouped', 'pollEnded', 'edited', 'scheduledNotePosted'] as (typeof groupedNotificationTypes[number])[]); +function undefOnMissing(packPromise: Promise): Promise { + return packPromise.catch(err => { + if (err instanceof EntityNotFoundError) return undefined; + throw err; + }); +} + @Injectable() export class NotificationEntityService implements OnModuleInit { private userEntityService: UserEntityService; @@ -75,9 +82,9 @@ export class NotificationEntityService implements OnModuleInit { const noteIfNeed = needsNote ? ( hint?.packedNotes != null ? hint.packedNotes.get(notification.noteId) - : this.noteEntityService.pack(notification.noteId, { id: meId }, { + : undefOnMissing(this.noteEntityService.pack(notification.noteId, { id: meId }, { detail: true, - }) + })) ) : undefined; // if the note has been deleted, don't show this notification if (needsNote && !noteIfNeed) return null; @@ -86,7 +93,7 @@ export class NotificationEntityService implements OnModuleInit { const userIfNeed = needsUser ? ( hint?.packedUsers != null ? hint.packedUsers.get(notification.notifierId) - : this.userEntityService.pack(notification.notifierId, { id: meId }) + : undefOnMissing(this.userEntityService.pack(notification.notifierId, { id: meId })) ) : undefined; // if the user has been deleted, don't show this notification if (needsUser && !userIfNeed) return null; @@ -96,7 +103,7 @@ export class NotificationEntityService implements OnModuleInit { const reactions = (await Promise.all(notification.reactions.map(async reaction => { const user = hint?.packedUsers != null ? hint.packedUsers.get(reaction.userId)! - : await this.userEntityService.pack(reaction.userId, { id: meId }); + : await undefOnMissing(this.userEntityService.pack(reaction.userId, { id: meId })); return { user, reaction: reaction.reaction, @@ -121,7 +128,7 @@ export class NotificationEntityService implements OnModuleInit { return packedUser; } - return this.userEntityService.pack(userId, { id: meId }); + return undefOnMissing(this.userEntityService.pack(userId, { id: meId })); }))).filter(x => x != null); // if all users have been deleted, don't show this notification if (users.length === 0) { @@ -140,10 +147,7 @@ export class NotificationEntityService implements OnModuleInit { const needsRole = notification.type === 'roleAssigned'; const role = needsRole - ? await this.roleEntityService.pack(notification.roleId).catch(err => { - if (err instanceof EntityNotFoundError) return undefined; - throw err; - }) + ? await undefOnMissing(this.roleEntityService.pack(notification.roleId)) : undefined; // if the role has been deleted, don't show this notification if (needsRole && !role) {