From 187c98e5acb4e20df263a914ae64c72a6414da60 Mon Sep 17 00:00:00 2001 From: Jacob Hall Date: Thu, 20 Feb 2025 15:09:48 -0500 Subject: do not notify mentioned users if a DM is not visible to them --- packages/backend/src/core/NoteEditService.ts | 1 + 1 file changed, 1 insertion(+) (limited to 'packages/backend/src/core/NoteEditService.ts') diff --git a/packages/backend/src/core/NoteEditService.ts b/packages/backend/src/core/NoteEditService.ts index 453ad5d9d0..3972ed49f8 100644 --- a/packages/backend/src/core/NoteEditService.ts +++ b/packages/backend/src/core/NoteEditService.ts @@ -801,6 +801,7 @@ export class NoteEditService implements OnApplicationShutdown { // TODO why is this unused? @bindThis private async createMentionedEvents(mentionedUsers: MinimumUser[], note: MiNote, nm: NotificationManager) { + // FIXME only users the note is visible to should receive a notification, same as when a note is created for (const u of mentionedUsers.filter(u => this.userEntityService.isLocalUser(u))) { const isThreadMuted = await this.noteThreadMutingsRepository.exists({ where: { -- cgit v1.2.3-freya From ea0371de34566ef6aee3682e9fe480fc0d347148 Mon Sep 17 00:00:00 2001 From: Hazelnoot Date: Tue, 1 Apr 2025 10:29:58 -0400 Subject: copy changes to NoteEditService.ts --- packages/backend/src/core/NoteEditService.ts | 108 +++++---------------------- 1 file changed, 17 insertions(+), 91 deletions(-) (limited to 'packages/backend/src/core/NoteEditService.ts') diff --git a/packages/backend/src/core/NoteEditService.ts b/packages/backend/src/core/NoteEditService.ts index ce6e6a9f03..7722beefc9 100644 --- a/packages/backend/src/core/NoteEditService.ts +++ b/packages/backend/src/core/NoteEditService.ts @@ -8,6 +8,7 @@ import * as mfm from '@transfem-org/sfm-js'; import { DataSource, In, IsNull, LessThan } from 'typeorm'; import * as Redis from 'ioredis'; import { Inject, Injectable, OnApplicationShutdown } from '@nestjs/common'; +import { UnrecoverableError } from 'bullmq'; import { extractMentions } from '@/misc/extract-mentions.js'; import { extractCustomEmojisFromMfm } from '@/misc/extract-custom-emojis-from-mfm.js'; import { extractHashtags } from '@/misc/extract-hashtags.js'; @@ -36,7 +37,6 @@ import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { ApRendererService } from '@/core/activitypub/ApRendererService.js'; import { ApDeliverManagerService } from '@/core/activitypub/ApDeliverManagerService.js'; -import { NoteReadService } from '@/core/NoteReadService.js'; import { RemoteUserResolveService } from '@/core/RemoteUserResolveService.js'; import { bindThis } from '@/decorators.js'; import { RoleService } from '@/core/RoleService.js'; @@ -203,7 +203,6 @@ export class NoteEditService implements OnApplicationShutdown { private globalEventService: GlobalEventService, private queueService: QueueService, private fanoutTimelineService: FanoutTimelineService, - private noteReadService: NoteReadService, private notificationService: NotificationService, private relayService: RelayService, private federatedInstanceService: FederatedInstanceService, @@ -233,7 +232,7 @@ export class NoteEditService implements OnApplicationShutdown { noindex: MiUser['noindex']; }, editid: MiNote['id'], data: Option, silent = false): Promise { if (!editid) { - throw new Error('fail'); + throw new UnrecoverableError('edit failed: missing editid'); } const oldnote = await this.notesRepository.findOneBy({ @@ -241,11 +240,11 @@ export class NoteEditService implements OnApplicationShutdown { }); if (oldnote == null) { - throw new Error('no such note'); + throw new UnrecoverableError('edit failed: missing oldnote'); } if (oldnote.userId !== user.id) { - throw new Error('not the author'); + throw new UnrecoverableError(`edit failed for ${oldnote.id}: user is not the note author`); } // we never want to change the replyId, so fetch the original "parent" @@ -276,12 +275,12 @@ export class NoteEditService implements OnApplicationShutdown { data.channel = await this.channelsRepository.findOneBy({ id: data.reply.channelId }); } + if (data.updatedAt == null) data.updatedAt = new Date(); if (data.visibility == null) data.visibility = 'public'; if (data.localOnly == null) data.localOnly = false; if (data.channel != null) data.visibility = 'public'; if (data.channel != null) data.visibleUsers = []; if (data.channel != null) data.localOnly = true; - if (data.updatedAt == null) data.updatedAt = new Date(); if (data.visibility === 'public' && data.channel == null) { const sensitiveWords = this.meta.sensitiveWords; @@ -310,7 +309,7 @@ export class NoteEditService implements OnApplicationShutdown { if (this.isRenote(data)) { if (data.renote.id === oldnote.id) { - throw new Error('A note can\'t renote itself'); + throw new UnrecoverableError(`edit failed for ${oldnote.id}: cannot renote itself`); } switch (data.renote.visibility) { @@ -406,10 +405,10 @@ export class NoteEditService implements OnApplicationShutdown { // Parse MFM if needed if (!tags || !emojis || !mentionedUsers) { - const tokens = data.text ? mfm.parse(data.text)! : []; - const cwTokens = data.cw ? mfm.parse(data.cw)! : []; + const tokens = data.text ? mfm.parse(data.text) : []; + const cwTokens = data.cw ? mfm.parse(data.cw) : []; const choiceTokens = data.poll && data.poll.choices - ? concat(data.poll.choices.map(choice => mfm.parse(choice)!)) + ? concat(data.poll.choices.map(choice => mfm.parse(choice))) : []; const combinedTokens = tokens.concat(cwTokens).concat(choiceTokens); @@ -424,7 +423,7 @@ export class NoteEditService implements OnApplicationShutdown { // if the host is media-silenced, custom emojis are not allowed if (this.utilityService.isMediaSilencedHost(this.meta.mediaSilencedHosts, user.host)) emojis = []; - tags = tags.filter(tag => Array.from(tag ?? '').length <= 128).splice(0, 32); + tags = tags.filter(tag => Array.from(tag).length <= 128).splice(0, 32); if (data.reply && (user.id !== data.reply.userId) && !mentionedUsers.some(u => u.id === data.reply!.userId)) { mentionedUsers.push(await this.usersRepository.findOneByOrFail({ id: data.reply!.userId })); @@ -467,10 +466,8 @@ export class NoteEditService implements OnApplicationShutdown { update.hasPoll = !!data.poll; } - // technically we should check if the two sets of files are - // different, or if their descriptions have changed. In practice - // this is good enough. - const filesChanged = oldnote.fileIds?.length || data.files?.length; + // TODO deep-compare files + const filesChanged = oldnote.fileIds.length || data.files?.length; const poll = await this.pollsRepository.findOneBy({ noteId: oldnote.id }); @@ -564,7 +561,7 @@ export class NoteEditService implements OnApplicationShutdown { noteVisibility: note.visibility, userId: user.id, userHost: user.host, - channelId: data.channel ? data.channel.id : null, + channelId: data.channel?.id ?? null, }); if (!oldnote.hasPoll) { @@ -628,44 +625,12 @@ export class NoteEditService implements OnApplicationShutdown { if (!silent) { if (this.userEntityService.isLocalUser(user)) this.activeUsersChart.write(user); - // 未読通知を作成 - if (data.visibility === 'specified') { - if (data.visibleUsers == null) throw new Error('invalid param'); - - for (const u of data.visibleUsers) { - // ローカルユーザーのみ - if (!this.userEntityService.isLocalUser(u)) continue; - - this.noteReadService.insertNoteUnread(u.id, note, { - isSpecified: true, - isMentioned: false, - }); - } - } else { - for (const u of mentionedUsers) { - // ローカルユーザーのみ - if (!this.userEntityService.isLocalUser(u)) continue; - - this.noteReadService.insertNoteUnread(u.id, note, { - isSpecified: false, - isMentioned: true, - }); - } - } - // Pack the note const noteObj = await this.noteEntityService.pack(note, null, { skipHide: true, withReactionAndUserPairCache: true }); - if (data.poll != null) { - this.globalEventService.publishNoteStream(note.id, 'updated', { - cw: note.cw, - text: note.text!, - }); - } else { - this.globalEventService.publishNoteStream(note.id, 'updated', { - cw: note.cw, - text: note.text!, - }); - } + this.globalEventService.publishNoteStream(note.id, 'updated', { + cw: note.cw, + text: note.text ?? '', + }); this.roleService.addNoteToRoleTimeline(noteObj); @@ -673,8 +638,6 @@ export class NoteEditService implements OnApplicationShutdown { const nm = new NotificationManager(this.mutingsRepository, this.notificationService, user, note); - //await this.createMentionedEvents(mentionedUsers, note, nm); - // If has in reply to note if (data.reply) { // 通知 @@ -697,7 +660,6 @@ export class NoteEditService implements OnApplicationShutdown { if (!isThreadMuted && !muted) { nm.push(data.reply.userId, 'edited'); this.globalEventService.publishMainStream(data.reply.userId, 'edited', noteObj); - this.webhookService.enqueueUserWebhook(data.reply.userId, 'reply', { note: noteObj }); } } @@ -803,42 +765,6 @@ export class NoteEditService implements OnApplicationShutdown { (note.files != null && note.files.length > 0); } - // TODO why is this unused? - @bindThis - private async createMentionedEvents(mentionedUsers: MinimumUser[], note: MiNote, nm: NotificationManager) { - // FIXME only users the note is visible to should receive a notification, same as when a note is created - for (const u of mentionedUsers.filter(u => this.userEntityService.isLocalUser(u))) { - const isThreadMuted = await this.noteThreadMutingsRepository.exists({ - where: { - userId: u.id, - threadId: note.threadId ?? note.id, - }, - }); - - const [ - userIdsWhoMeMuting, - ] = u.id ? await Promise.all([ - this.cacheService.userMutingsCache.fetch(u.id), - ]) : [new Set()]; - - const muted = isUserRelated(note, userIdsWhoMeMuting); - - if (isThreadMuted || muted) { - continue; - } - - const detailPackedNote = await this.noteEntityService.pack(note, u, { - detail: true, - }); - - this.globalEventService.publishMainStream(u.id, 'edited', detailPackedNote); - this.webhookService.enqueueUserWebhook(u.id, 'edited', { note: detailPackedNote }); - - // Create notification - nm.push(u.id, 'edited'); - } - } - @bindThis private async renderNoteOrRenoteActivity(data: Option, note: MiNote, user: MiUser) { if (data.localOnly) return null; -- cgit v1.2.3-freya From fffa6c97fedc38a22fd5d7af4ddba8c9fc0c4a80 Mon Sep 17 00:00:00 2001 From: Hazelnoot Date: Tue, 1 Apr 2025 10:55:56 -0400 Subject: copy changes to notes/edit.ts --- packages/backend/src/core/NoteEditService.ts | 4 ++-- packages/backend/src/server/api/endpoints/notes/edit.ts | 6 ++---- 2 files changed, 4 insertions(+), 6 deletions(-) (limited to 'packages/backend/src/core/NoteEditService.ts') diff --git a/packages/backend/src/core/NoteEditService.ts b/packages/backend/src/core/NoteEditService.ts index 7722beefc9..e70ecf396d 100644 --- a/packages/backend/src/core/NoteEditService.ts +++ b/packages/backend/src/core/NoteEditService.ts @@ -240,11 +240,11 @@ export class NoteEditService implements OnApplicationShutdown { }); if (oldnote == null) { - throw new UnrecoverableError('edit failed: missing oldnote'); + throw new UnrecoverableError(`edit failed for ${editid}: missing oldnote`); } if (oldnote.userId !== user.id) { - throw new UnrecoverableError(`edit failed for ${oldnote.id}: user is not the note author`); + throw new UnrecoverableError(`edit failed for ${editid}: user is not the note author`); } // we never want to change the replyId, so fetch the original "parent" diff --git a/packages/backend/src/server/api/endpoints/notes/edit.ts b/packages/backend/src/server/api/endpoints/notes/edit.ts index 7b13d3a4f4..2c01b26584 100644 --- a/packages/backend/src/server/api/endpoints/notes/edit.ts +++ b/packages/backend/src/server/api/endpoints/notes/edit.ts @@ -31,13 +31,11 @@ export const meta = { res: { type: 'object', - optional: false, - nullable: false, + optional: false, nullable: false, properties: { createdNote: { type: 'object', - optional: false, - nullable: false, + optional: false, nullable: false, ref: 'Note', }, }, -- cgit v1.2.3-freya From c302a5c2d7696bc9dddeabe914b92ad2fdc0b0ba Mon Sep 17 00:00:00 2001 From: Hazelnoot Date: Mon, 5 May 2025 17:44:00 -0400 Subject: reorder relay activities to avoid delivery race condition --- packages/backend/src/core/AccountMoveService.ts | 3 ++- packages/backend/src/core/AccountUpdateService.ts | 9 +++------ packages/backend/src/core/NoteCreateService.ts | 12 ++++++------ packages/backend/src/core/NoteDeleteService.ts | 6 +++--- packages/backend/src/core/NoteEditService.ts | 12 ++++++------ packages/backend/src/core/NotePiningService.ts | 17 +++++++---------- packages/backend/src/core/PollService.ts | 9 +++------ .../src/core/activitypub/models/ApNoteService.ts | 4 ++-- packages/backend/src/misc/promise-tracker.ts | 4 ++++ packages/backend/src/server/api/endpoints/i/update.ts | 2 +- .../src/server/api/endpoints/notes/polls/vote.ts | 2 +- 11 files changed, 38 insertions(+), 42 deletions(-) (limited to 'packages/backend/src/core/NoteEditService.ts') diff --git a/packages/backend/src/core/AccountMoveService.ts b/packages/backend/src/core/AccountMoveService.ts index 7bf33e13c5..738026f753 100644 --- a/packages/backend/src/core/AccountMoveService.ts +++ b/packages/backend/src/core/AccountMoveService.ts @@ -95,11 +95,12 @@ export class AccountMoveService { const srcPerson = await this.apRendererService.renderPerson(src); const updateAct = this.apRendererService.addContext(this.apRendererService.renderUpdate(srcPerson, src)); await this.apDeliverManagerService.deliverToFollowers(src, updateAct); - this.relayService.deliverToRelays(src, updateAct); + await this.relayService.deliverToRelays(src, updateAct); // Deliver Move activity to the followers of the old account const moveAct = this.apRendererService.addContext(this.apRendererService.renderMove(src, dst)); await this.apDeliverManagerService.deliverToFollowers(src, moveAct); + await this.relayService.deliverToRelays(src, moveAct); // Publish meUpdated event const iObj = await this.userEntityService.pack(src.id, src, { schema: 'MeDetailed', includeSecrets: true }); diff --git a/packages/backend/src/core/AccountUpdateService.ts b/packages/backend/src/core/AccountUpdateService.ts index 69a57b4854..ef2962fc12 100644 --- a/packages/backend/src/core/AccountUpdateService.ts +++ b/packages/backend/src/core/AccountUpdateService.ts @@ -27,15 +27,12 @@ export class AccountUpdateService { } @bindThis - public async publishToFollowers(userId: MiUser['id']) { - const user = await this.usersRepository.findOneBy({ id: userId }); - if (user == null) throw new Error('user not found'); - + public async publishToFollowers(user: MiUser) { // フォロワーがリモートユーザーかつ投稿者がローカルユーザーならUpdateを配信 if (this.userEntityService.isLocalUser(user)) { const content = this.apRendererService.addContext(this.apRendererService.renderUpdate(await this.apRendererService.renderPerson(user), user)); - this.apDeliverManagerService.deliverToFollowers(user, content); - this.relayService.deliverToRelays(user, content); + await this.apDeliverManagerService.deliverToFollowers(user, content); + await this.relayService.deliverToRelays(user, content); } } } diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts index fd6300483f..ed97908f66 100644 --- a/packages/backend/src/core/NoteCreateService.ts +++ b/packages/backend/src/core/NoteCreateService.ts @@ -51,7 +51,7 @@ import { FanoutTimelineService } from '@/core/FanoutTimelineService.js'; import { UtilityService } from '@/core/UtilityService.js'; import { UserBlockingService } from '@/core/UserBlockingService.js'; import { isReply } from '@/misc/is-reply.js'; -import { trackPromise } from '@/misc/promise-tracker.js'; +import { trackTask } from '@/misc/promise-tracker.js'; import { isUserRelated } from '@/misc/is-user-related.js'; import { IdentifiableError } from '@/misc/identifiable-error.js'; import { LatestNoteService } from '@/core/LatestNoteService.js'; @@ -729,7 +729,7 @@ export class NoteCreateService implements OnApplicationShutdown { //#region AP deliver if (!data.localOnly && this.userEntityService.isLocalUser(user)) { - (async () => { + trackTask(async () => { const noteActivity = await this.renderNoteOrRenoteActivity(data, note, user); const dm = this.apDeliverManagerService.createDeliverManager(user, noteActivity); @@ -755,12 +755,12 @@ export class NoteCreateService implements OnApplicationShutdown { dm.addFollowersRecipe(); } + await dm.execute(); + if (['public'].includes(note.visibility)) { - this.relayService.deliverToRelays(user, noteActivity); + await this.relayService.deliverToRelays(user, noteActivity); } - - trackPromise(dm.execute()); - })(); + }); } //#endregion } diff --git a/packages/backend/src/core/NoteDeleteService.ts b/packages/backend/src/core/NoteDeleteService.ts index 8ec05c88dc..9b6c4754d1 100644 --- a/packages/backend/src/core/NoteDeleteService.ts +++ b/packages/backend/src/core/NoteDeleteService.ts @@ -247,11 +247,11 @@ export class NoteDeleteService { @bindThis private async deliverToConcerned(user: { id: MiLocalUser['id']; host: null; }, note: MiNote, content: any) { - this.apDeliverManagerService.deliverToFollowers(user, content); - this.relayService.deliverToRelays(user, content); - this.apDeliverManagerService.deliverToUsers(user, content, [ + await this.apDeliverManagerService.deliverToFollowers(user, content); + await this.apDeliverManagerService.deliverToUsers(user, content, [ ...await this.getMentionedRemoteUsers(note), ...await this.getRenotedOrRepliedRemoteUsers(note), ]); + await this.relayService.deliverToRelays(user, content); } } diff --git a/packages/backend/src/core/NoteEditService.ts b/packages/backend/src/core/NoteEditService.ts index e70ecf396d..332560154d 100644 --- a/packages/backend/src/core/NoteEditService.ts +++ b/packages/backend/src/core/NoteEditService.ts @@ -46,7 +46,7 @@ import { UtilityService } from '@/core/UtilityService.js'; import { UserBlockingService } from '@/core/UserBlockingService.js'; import { CacheService } from '@/core/CacheService.js'; import { isReply } from '@/misc/is-reply.js'; -import { trackPromise } from '@/misc/promise-tracker.js'; +import { trackTask } from '@/misc/promise-tracker.js'; import { isUserRelated } from '@/misc/is-user-related.js'; import { IdentifiableError } from '@/misc/identifiable-error.js'; import { LatestNoteService } from '@/core/LatestNoteService.js'; @@ -669,7 +669,7 @@ export class NoteEditService implements OnApplicationShutdown { //#region AP deliver if (!data.localOnly && this.userEntityService.isLocalUser(user)) { - (async () => { + trackTask(async () => { const noteActivity = await this.renderNoteOrRenoteActivity(data, note, user); const dm = this.apDeliverManagerService.createDeliverManager(user, noteActivity); @@ -713,12 +713,12 @@ export class NoteEditService implements OnApplicationShutdown { } } + await dm.execute(); + if (['public'].includes(note.visibility)) { - this.relayService.deliverToRelays(user, noteActivity); + await this.relayService.deliverToRelays(user, noteActivity); } - - trackPromise(dm.execute()); - })(); + }); } //#endregion } diff --git a/packages/backend/src/core/NotePiningService.ts b/packages/backend/src/core/NotePiningService.ts index d38b48b65d..6ab7268254 100644 --- a/packages/backend/src/core/NotePiningService.ts +++ b/packages/backend/src/core/NotePiningService.ts @@ -49,7 +49,7 @@ export class NotePiningService { * @param noteId */ @bindThis - public async addPinned(user: { id: MiUser['id']; host: MiUser['host']; }, noteId: MiNote['id']) { + public async addPinned(user: MiUser, noteId: MiNote['id']) { // Fetch pinee const note = await this.notesRepository.findOneBy({ id: noteId, @@ -78,7 +78,7 @@ export class NotePiningService { // Deliver to remote followers if (this.userEntityService.isLocalUser(user) && !note.localOnly && ['public', 'home'].includes(note.visibility)) { - this.deliverPinnedChange(user.id, note.id, true); + this.deliverPinnedChange(user, note.id, true); } } @@ -88,7 +88,7 @@ export class NotePiningService { * @param noteId */ @bindThis - public async removePinned(user: { id: MiUser['id']; host: MiUser['host']; }, noteId: MiNote['id']) { + public async removePinned(user: MiUser, noteId: MiNote['id']) { // Fetch unpinee const note = await this.notesRepository.findOneBy({ id: noteId, @@ -106,22 +106,19 @@ export class NotePiningService { // Deliver to remote followers if (this.userEntityService.isLocalUser(user) && !note.localOnly && ['public', 'home'].includes(note.visibility)) { - this.deliverPinnedChange(user.id, noteId, false); + this.deliverPinnedChange(user, noteId, false); } } @bindThis - public async deliverPinnedChange(userId: MiUser['id'], noteId: MiNote['id'], isAddition: boolean) { - const user = await this.usersRepository.findOneBy({ id: userId }); - if (user == null) throw new Error('user not found'); - + public async deliverPinnedChange(user: MiUser, noteId: MiNote['id'], isAddition: boolean) { if (!this.userEntityService.isLocalUser(user)) return; const target = `${this.config.url}/users/${user.id}/collections/featured`; const item = `${this.config.url}/notes/${noteId}`; const content = this.apRendererService.addContext(isAddition ? this.apRendererService.renderAdd(user, target, item) : this.apRendererService.renderRemove(user, target, item)); - this.apDeliverManagerService.deliverToFollowers(user, content); - this.relayService.deliverToRelays(user, content); + await this.apDeliverManagerService.deliverToFollowers(user, content); + await this.relayService.deliverToRelays(user, content); } } diff --git a/packages/backend/src/core/PollService.ts b/packages/backend/src/core/PollService.ts index d6364613bd..33262a4804 100644 --- a/packages/backend/src/core/PollService.ts +++ b/packages/backend/src/core/PollService.ts @@ -90,10 +90,7 @@ export class PollService { } @bindThis - public async deliverQuestionUpdate(noteId: MiNote['id']) { - const note = await this.notesRepository.findOneBy({ id: noteId }); - if (note == null) throw new Error('note not found'); - + public async deliverQuestionUpdate(note: MiNote) { if (note.localOnly) return; const user = await this.usersRepository.findOneBy({ id: note.userId }); @@ -101,8 +98,8 @@ export class PollService { if (this.userEntityService.isLocalUser(user)) { const content = this.apRendererService.addContext(this.apRendererService.renderUpdate(await this.apRendererService.renderNote(note, user, false), user)); - this.apDeliverManagerService.deliverToFollowers(user, content); - this.relayService.deliverToRelays(user, content); + await this.apDeliverManagerService.deliverToFollowers(user, content); + await this.relayService.deliverToRelays(user, content); } } } diff --git a/packages/backend/src/core/activitypub/models/ApNoteService.ts b/packages/backend/src/core/activitypub/models/ApNoteService.ts index 11f5bbd943..f6152e3888 100644 --- a/packages/backend/src/core/activitypub/models/ApNoteService.ts +++ b/packages/backend/src/core/activitypub/models/ApNoteService.ts @@ -296,7 +296,7 @@ export class ApNoteService { await this.pollService.vote(actor, reply, index); // リモートフォロワーにUpdate配信 - this.pollService.deliverQuestionUpdate(reply.id); + this.pollService.deliverQuestionUpdate(reply); } return null; }; @@ -493,7 +493,7 @@ export class ApNoteService { await this.pollService.vote(actor, reply, index); // リモートフォロワーにUpdate配信 - this.pollService.deliverQuestionUpdate(reply.id); + this.pollService.deliverQuestionUpdate(reply); } return null; }; diff --git a/packages/backend/src/misc/promise-tracker.ts b/packages/backend/src/misc/promise-tracker.ts index 8a52ca703e..76b4dd810c 100644 --- a/packages/backend/src/misc/promise-tracker.ts +++ b/packages/backend/src/misc/promise-tracker.ts @@ -5,6 +5,10 @@ const promiseRefs: Set>> = new Set(); +export function trackTask(task: () => Promise): void { + trackPromise(task()); +} + /** * This tracks promises that other modules decided not to wait for, * and makes sure they are all settled before fully closing down the server. diff --git a/packages/backend/src/server/api/endpoints/i/update.ts b/packages/backend/src/server/api/endpoints/i/update.ts index 094c3da8e6..5f93597fd7 100644 --- a/packages/backend/src/server/api/endpoints/i/update.ts +++ b/packages/backend/src/server/api/endpoints/i/update.ts @@ -614,7 +614,7 @@ export default class extends Endpoint { // eslint- // フォロワーにUpdateを配信 if (this.userNeedsPublishing(user, updates) || this.profileNeedsPublishing(profile, updatedProfile)) { - this.accountUpdateService.publishToFollowers(user.id); + this.accountUpdateService.publishToFollowers(user); } return iObj; diff --git a/packages/backend/src/server/api/endpoints/notes/polls/vote.ts b/packages/backend/src/server/api/endpoints/notes/polls/vote.ts index a5014a490f..0b318304f3 100644 --- a/packages/backend/src/server/api/endpoints/notes/polls/vote.ts +++ b/packages/backend/src/server/api/endpoints/notes/polls/vote.ts @@ -174,7 +174,7 @@ export default class extends Endpoint { // eslint- } // リモートフォロワーにUpdate配信 - this.pollService.deliverQuestionUpdate(note.id); + this.pollService.deliverQuestionUpdate(note); }); } } -- cgit v1.2.3-freya From 1fe39ed4327b64e8aff4bee7c9ffe507f04d23a7 Mon Sep 17 00:00:00 2001 From: Hazelnoot Date: Thu, 8 May 2025 16:34:40 -0400 Subject: re-fetch notes after create/edit to ensure they have all fields populated --- packages/backend/src/core/NoteCreateService.ts | 5 +++-- packages/backend/src/core/NoteEditService.ts | 7 +++++-- 2 files changed, 8 insertions(+), 4 deletions(-) (limited to 'packages/backend/src/core/NoteEditService.ts') diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts index ed97908f66..a342bc1912 100644 --- a/packages/backend/src/core/NoteCreateService.ts +++ b/packages/backend/src/core/NoteCreateService.ts @@ -227,7 +227,7 @@ export class NoteCreateService implements OnApplicationShutdown { } @bindThis - public async create(user: MiUser & { + public async create(user: MiUser & { id: MiUser['id']; username: MiUser['username']; host: MiUser['host']; @@ -539,7 +539,8 @@ export class NoteCreateService implements OnApplicationShutdown { await this.notesRepository.insert(insert); } - return insert; + // Re-fetch note to get the default values of null / unset fields. + return await this.notesRepository.findOneByOrFail({ id: insert.id }); } catch (e) { // duplicate key error if (isDuplicateKeyValueError(e)) { diff --git a/packages/backend/src/core/NoteEditService.ts b/packages/backend/src/core/NoteEditService.ts index 332560154d..e9637c56c7 100644 --- a/packages/backend/src/core/NoteEditService.ts +++ b/packages/backend/src/core/NoteEditService.ts @@ -574,12 +574,15 @@ export class NoteEditService implements OnApplicationShutdown { await this.notesRepository.update(oldnote.id, note); } + // Re-fetch note to get the default values of null / unset fields. + const edited = await this.notesRepository.findOneByOrFail({ id: note.id }); + setImmediate('post edited', { signal: this.#shutdownController.signal }).then( - () => this.postNoteEdited(note, oldnote, user, data, silent, tags!, mentionedUsers!), + () => this.postNoteEdited(edited, oldnote, user, data, silent, tags!, mentionedUsers!), () => { /* aborted, ignore this */ }, ); - return note; + return edited; } else { return oldnote; } -- cgit v1.2.3-freya From b4bce57fcb35e2bd7021eef1b608bd17d3533b10 Mon Sep 17 00:00:00 2001 From: Hazelnoot Date: Sun, 11 May 2025 05:06:58 -0400 Subject: update user activity on Renote, Un-Renote, Note Edit, React, and Un-React --- packages/backend/src/core/NoteCreateService.ts | 2 ++ packages/backend/src/core/NoteDeleteService.ts | 2 ++ packages/backend/src/core/NoteEditService.ts | 2 ++ packages/backend/src/core/ReactionService.ts | 4 ++++ 4 files changed, 10 insertions(+) (limited to 'packages/backend/src/core/NoteEditService.ts') diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts index b810ae0b5b..4514f3decd 100644 --- a/packages/backend/src/core/NoteCreateService.ts +++ b/packages/backend/src/core/NoteCreateService.ts @@ -592,6 +592,8 @@ export class NoteCreateService implements OnApplicationShutdown { if (!this.isRenote(note) || this.isQuote(note)) { // Increment notes count (user) this.incNotesCountOfUser(user); + } else { + this.usersRepository.update({ id: user.id }, { updatedAt: new Date() }); } this.pushToTl(note, user); diff --git a/packages/backend/src/core/NoteDeleteService.ts b/packages/backend/src/core/NoteDeleteService.ts index 1c634602f3..9ce8cb6731 100644 --- a/packages/backend/src/core/NoteDeleteService.ts +++ b/packages/backend/src/core/NoteDeleteService.ts @@ -127,6 +127,8 @@ export class NoteDeleteService { if (!isRenote(note) || isQuote(note)) { // Decrement notes count (user) this.decNotesCountOfUser(user); + } else { + this.usersRepository.update({ id: user.id }, { updatedAt: new Date() }); } if (this.meta.enableStatsForFederatedInstances) { diff --git a/packages/backend/src/core/NoteEditService.ts b/packages/backend/src/core/NoteEditService.ts index e9637c56c7..58233b90ee 100644 --- a/packages/backend/src/core/NoteEditService.ts +++ b/packages/backend/src/core/NoteEditService.ts @@ -610,6 +610,8 @@ export class NoteEditService implements OnApplicationShutdown { } } + this.usersRepository.update({ id: user.id }, { updatedAt: new Date() }); + // ハッシュタグ更新 this.pushToTl(note, user); diff --git a/packages/backend/src/core/ReactionService.ts b/packages/backend/src/core/ReactionService.ts index 6cc4ef4205..373fea8605 100644 --- a/packages/backend/src/core/ReactionService.ts +++ b/packages/backend/src/core/ReactionService.ts @@ -212,6 +212,8 @@ export class ReactionService { .execute(); } + this.usersRepository.update({ id: user.id }, { updatedAt: new Date() }); + // 30%の確率、セルフではない、3日以内に投稿されたノートの場合ハイライト用ランキング更新 if ( Math.random() < 0.3 && @@ -330,6 +332,8 @@ export class ReactionService { .execute(); } + this.usersRepository.update({ id: user.id }, { updatedAt: new Date() }); + this.globalEventService.publishNoteStream(note.id, 'unreacted', { reaction: this.decodeReaction(exist.reaction).reaction, userId: user.id, -- cgit v1.2.3-freya