From 0544f3fc3d5667b31e44d25bb0fab60d9d842bda Mon Sep 17 00:00:00 2001 From: anatawa12 Date: Sat, 2 Aug 2025 21:59:39 +0900 Subject: [PATCH] feat: add to 'deleted_note' table on remove --- .../backend/src/core/NoteDeleteService.ts | 27 +++++++++++--- .../CleanRemoteNotesProcessorService.ts | 37 ++++++++++++++++--- 2 files changed, 54 insertions(+), 10 deletions(-) diff --git a/packages/backend/src/core/NoteDeleteService.ts b/packages/backend/src/core/NoteDeleteService.ts index af1f0eda9a..a56614d3f2 100644 --- a/packages/backend/src/core/NoteDeleteService.ts +++ b/packages/backend/src/core/NoteDeleteService.ts @@ -3,10 +3,12 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { Brackets, In, IsNull, Not } from 'typeorm'; +import { Brackets, DataSource, In, IsNull, Not } from 'typeorm'; import { Injectable, Inject } from '@nestjs/common'; import type { MiUser, MiLocalUser, MiRemoteUser } from '@/models/User.js'; -import type { MiNote, IMentionedRemoteUsers } from '@/models/Note.js'; +import { MiNote } from '@/models/Note.js'; +import { MiDeletedNote } from '@/models/DeletedNote.js'; +import type { IMentionedRemoteUsers } from '@/models/Note.js'; import type { InstancesRepository, MiMeta, NotesRepository, UsersRepository } from '@/models/_.js'; import { RelayService } from '@/core/RelayService.js'; import { FederatedInstanceService } from '@/core/FederatedInstanceService.js'; @@ -30,6 +32,9 @@ export class NoteDeleteService { @Inject(DI.config) private config: Config, + @Inject(DI.db) + private db: DataSource, + @Inject(DI.meta) private meta: MiMeta, @@ -110,9 +115,21 @@ export class NoteDeleteService { this.searchService.unindexNote(note); - await this.notesRepository.delete({ - id: note.id, - userId: user.id, + await this.db.transaction(async transaction => { + await transaction.delete(MiNote, { + id: note.id, + userId: user.id, + }); + await transaction.save(MiDeletedNote, { + id: note.id, + deletedAt: new Date(), + replyId: note.replyId, + renoteId: note.renoteId, + userId: note.userId, + localOnly: note.localOnly, + uri: note.uri, + url: note.url, + }); }); if (deleter && (note.userId !== deleter.id)) { diff --git a/packages/backend/src/queue/processors/CleanRemoteNotesProcessorService.ts b/packages/backend/src/queue/processors/CleanRemoteNotesProcessorService.ts index 5b682e20b8..122aee3af5 100644 --- a/packages/backend/src/queue/processors/CleanRemoteNotesProcessorService.ts +++ b/packages/backend/src/queue/processors/CleanRemoteNotesProcessorService.ts @@ -5,9 +5,11 @@ import { setTimeout } from 'node:timers/promises'; import { Inject, Injectable } from '@nestjs/common'; -import { And, In, IsNull, LessThan, MoreThan, Not } from 'typeorm'; +import { DataSource, In, IsNull, LessThan, Not } from 'typeorm'; import { DI } from '@/di-symbols.js'; -import type { MiMeta, MiNote, NoteFavoritesRepository, NotesRepository, UserNotePiningsRepository } from '@/models/_.js'; +import { MiNote } from '@/models/Note.js'; +import { MiDeletedNote } from '@/models/DeletedNote.js'; +import type { MiMeta, NoteFavoritesRepository, NotesRepository, UserNotePiningsRepository } from '@/models/_.js'; import type Logger from '@/logger.js'; import { bindThis } from '@/decorators.js'; import { IdService } from '@/core/IdService.js'; @@ -22,6 +24,9 @@ export class CleanRemoteNotesProcessorService { @Inject(DI.meta) private meta: MiMeta, + @Inject(DI.db) + private db: DataSource, + @Inject(DI.notesRepository) private notesRepository: NotesRepository, @@ -72,7 +77,17 @@ export class CleanRemoteNotesProcessorService { while (true) { const batchBeginAt = Date.now(); - let notes: Pick[] = await this.notesRepository.find({ + const selectColumns = [...[ + 'id', + 'replyId', + 'renoteId', + 'userId', + 'localOnly', + 'uri', + 'url', + 'channelId', + ] as const]; + let notes: Pick[] = await this.notesRepository.find({ where: { id: LessThan(cursor), userHost: Not(IsNull()), @@ -85,7 +100,7 @@ export class CleanRemoteNotesProcessorService { // https://github.com/misskey-dev/misskey/pull/16292#issuecomment-3139376314 id: -1, }, - select: ['id'], + select: selectColumns, }); const fetchedCount = notes.length; @@ -131,7 +146,19 @@ export class CleanRemoteNotesProcessorService { }); if (notes.length > 0) { - await this.notesRepository.delete(notes.map(note => note.id)); + await this.db.transaction(async (transaction) => { + await transaction.save(MiDeletedNote, notes.map(note => ({ + id: note.id, + deletedAt: null, // This is existing note on the remote, so we set deletedAt to null. + replyId: note.replyId, + renoteId: note.renoteId, + userId: note.userId, + localOnly: note.localOnly, + uri: note.uri, + url: note.url, + }))); + await transaction.delete(MiNote, notes.map(note => note.id)); + }); for (const note of notes) { const t = this.idService.parse(note.id).date.getTime();