forked from FoundKeyGang/FoundKey
activitypub: send all cascade deletes
The `deleteNotes` function would not correctly handle cases where cascade deleted notes were from a different user than the initially deleted note. Changelog: Fixed
This commit is contained in:
parent
8b16ead35c
commit
b7dc3cca22
4 changed files with 38 additions and 19 deletions
|
@ -29,7 +29,7 @@ export default async function(actor: IRemoteUser, uri: string): Promise<string>
|
|||
return 'skip: cant delete other actors note';
|
||||
}
|
||||
|
||||
await deleteNotes([note], actor);
|
||||
await deleteNotes([note]);
|
||||
return 'ok: note deleted';
|
||||
}
|
||||
} finally {
|
||||
|
|
|
@ -13,6 +13,6 @@ export const undoAnnounce = async (actor: IRemoteUser, activity: IAnnounce): Pro
|
|||
|
||||
if (!note) return 'skip: no such Announce';
|
||||
|
||||
await deleteNotes([note], actor);
|
||||
await deleteNotes([note]);
|
||||
return 'ok: deleted';
|
||||
};
|
||||
|
|
|
@ -50,5 +50,5 @@ export default define(meta, paramDef, async (ps, user) => {
|
|||
|
||||
if (renotes.length === 0) return;
|
||||
|
||||
await deleteNotes(renotes, user);
|
||||
await deleteNotes(renotes);
|
||||
});
|
||||
|
|
|
@ -15,25 +15,43 @@ import { DeliverManager } from '@/remote/activitypub/deliver-manager.js';
|
|||
import { countSameRenotes } from '@/misc/count-same-renotes.js';
|
||||
import { registerOrFetchInstanceDoc } from '../register-or-fetch-instance-doc.js';
|
||||
import { deliverMultipleToRelays } from '../relay.js';
|
||||
import { groupByX } from '@/prelude/array.js';
|
||||
|
||||
/**
|
||||
* Delete several notes of the same user.
|
||||
* @param notes Array of notes to be deleted.
|
||||
* @param user Author of the notes. Will be fetched if not provided.
|
||||
*/
|
||||
export async function deleteNotes(notes: Note[], user?: User): Promise<void> {
|
||||
if (notes.length === 0) return;
|
||||
|
||||
const fetchedUser = user ?? await Users.findOneByOrFail({ id: notes[0].userId });
|
||||
|
||||
const cascadingNotes = await Promise.all(
|
||||
export async function deleteNotes(notes: Note[], cascading?: Note[]): Promise<void> {
|
||||
const cascadingNotes = cascading ?? await Promise.all(
|
||||
notes.map(note => findCascadingNotes(note))
|
||||
).then(res => res.flat());
|
||||
|
||||
const allNotes = notes.concat(cascadingNotes);
|
||||
if (allNotes.length === 0) return;
|
||||
|
||||
// check if notes of different users are affected, they need to be handled separately
|
||||
const userId = allNotes[0].userId;
|
||||
if (allNotes.some(note => note.userId !== userId)) {
|
||||
const notesGrouped = groupByX(notes, x => x.userId);
|
||||
const cascadingGrouped = groupByX(cascadingNotes, x => x.userId);
|
||||
|
||||
const users = [...new Set([...Object.keys(notesGrouped), ...Object.keys(cascadingGrouped)])];
|
||||
|
||||
await Promise.all(
|
||||
users.map(userId =>
|
||||
deleteNotes(notesGrouped[userId] ?? [], cascadingGrouped[userId] ?? [])
|
||||
)
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// all of the notes left over are by a single user
|
||||
const fetchedUser = await Users.findOneByOrFail({ id: userId });
|
||||
|
||||
// perform side effects for notes and cascaded notes
|
||||
await Promise.all(
|
||||
notes.concat(cascadingNotes)
|
||||
.map(note => deletionSideEffects(note, fetchedUser))
|
||||
allNotes.map(note => deletionSideEffects(note, fetchedUser))
|
||||
);
|
||||
|
||||
// Compute delivery content for later.
|
||||
|
@ -41,7 +59,7 @@ export async function deleteNotes(notes: Note[], user?: User): Promise<void> {
|
|||
// the database since we may need some information from parent
|
||||
// notes that cause this one to be cascade-deleted.
|
||||
let content = await Promise.all(
|
||||
notes.concat(cascadingNotes)
|
||||
allNotes
|
||||
// only deliver for local notes that are not local-only
|
||||
.filter(note => note.userHost == null && !note.localOnly)
|
||||
.map(async note => {
|
||||
|
@ -65,8 +83,7 @@ export async function deleteNotes(notes: Note[], user?: User): Promise<void> {
|
|||
manager.addEveryone();
|
||||
// Check mentioned users, since not all may have a shared inbox.
|
||||
await Promise.all(
|
||||
notes.concat(cascadingNotes)
|
||||
.map(note => getMentionedRemoteUsers(note))
|
||||
allNotes.map(note => getMentionedRemoteUsers(note))
|
||||
)
|
||||
.then(remoteUsers => {
|
||||
remoteUsers.flat()
|
||||
|
@ -77,15 +94,17 @@ export async function deleteNotes(notes: Note[], user?: User): Promise<void> {
|
|||
// It is important that this is done before delivering the activities.
|
||||
// Otherwise there might be a race condition where we tell someone
|
||||
// the note exists and they can successfully fetch it.
|
||||
await Notes.delete({
|
||||
id: In(notes.map(x => x.id)),
|
||||
userId: fetchedUser.id,
|
||||
});
|
||||
if (notes.length > 0) {
|
||||
await Notes.delete({
|
||||
id: In(notes.map(x => x.id)),
|
||||
userId: fetchedUser.id,
|
||||
});
|
||||
}
|
||||
|
||||
// deliver the previously computed content
|
||||
await Promise.all([
|
||||
manager.execute(),
|
||||
deliverMultipleToRelays(user, content),
|
||||
deliverMultipleToRelays(fetchedUser, content),
|
||||
]);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue