feat: pack and response deleted note as deleted note
This commit is contained in:
parent
0544f3fc3d
commit
da43b554f2
|
@ -129,6 +129,7 @@ export class NoteDeleteService {
|
|||
localOnly: note.localOnly,
|
||||
uri: note.uri,
|
||||
url: note.url,
|
||||
channelId: note.channelId,
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ import type { Packed } from '@/misc/json-schema.js';
|
|||
import { awaitAll } from '@/misc/prelude/await-all.js';
|
||||
import type { MiUser } from '@/models/User.js';
|
||||
import type { MiNote } from '@/models/Note.js';
|
||||
import type { UsersRepository, NotesRepository, FollowingsRepository, PollsRepository, PollVotesRepository, NoteReactionsRepository, ChannelsRepository, MiMeta } from '@/models/_.js';
|
||||
import type { UsersRepository, NotesRepository, FollowingsRepository, PollsRepository, PollVotesRepository, NoteReactionsRepository, ChannelsRepository, MiMeta, DeletedNotesRepository, MiDeletedNote } from '@/models/_.js';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
import { DebounceLoader } from '@/misc/loader.js';
|
||||
import { IdService } from '@/core/IdService.js';
|
||||
|
@ -79,6 +79,9 @@ export class NoteEntityService implements OnModuleInit {
|
|||
@Inject(DI.notesRepository)
|
||||
private notesRepository: NotesRepository,
|
||||
|
||||
@Inject(DI.deletedNotesRepository)
|
||||
private deletedNotesRepository: DeletedNotesRepository,
|
||||
|
||||
@Inject(DI.followingsRepository)
|
||||
private followingsRepository: FollowingsRepository,
|
||||
|
||||
|
@ -377,7 +380,19 @@ export class NoteEntityService implements OnModuleInit {
|
|||
}, options);
|
||||
|
||||
const meId = me ? me.id : null;
|
||||
const note = typeof src === 'object' ? src : await this.noteLoader.load(src);
|
||||
let note: MiNote;
|
||||
if (typeof src === 'object') {
|
||||
note = src;
|
||||
} else {
|
||||
try {
|
||||
note = await this.noteLoader.load(src);
|
||||
} catch (err) {
|
||||
if (err instanceof EntityNotFoundError) {
|
||||
return this.packDeletedNote(src);
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
const host = note.userHost;
|
||||
|
||||
const bufferedReactions = opts._hint_?.bufferedReactions != null
|
||||
|
@ -448,20 +463,20 @@ export class NoteEntityService implements OnModuleInit {
|
|||
clippedCount: note.clippedCount,
|
||||
|
||||
// そもそもJOINしていない場合はundefined、JOINしたけど存在していなかった場合はnullで区別される
|
||||
reply: (note.replyId && note.reply === null) ? null : note.replyId ? nullIfEntityNotFound(this.pack(note.reply ?? note.replyId, me, {
|
||||
reply: note.replyId ? this.pack(note.reply ?? note.replyId, me, {
|
||||
detail: false,
|
||||
skipHide: opts.skipHide,
|
||||
withReactionAndUserPairCache: opts.withReactionAndUserPairCache,
|
||||
_hint_: options?._hint_,
|
||||
})) : undefined,
|
||||
}) : undefined,
|
||||
|
||||
// そもそもJOINしていない場合はundefined、JOINしたけど存在していなかった場合はnullで区別される
|
||||
renote: (note.renoteId && note.renote === null) ? null : note.renoteId ? nullIfEntityNotFound(this.pack(note.renote ?? note.renoteId, me, {
|
||||
renote: note.renoteId ? this.pack(note.renote ?? note.renoteId, me, {
|
||||
detail: true,
|
||||
skipHide: opts.skipHide,
|
||||
withReactionAndUserPairCache: opts.withReactionAndUserPairCache,
|
||||
_hint_: options?._hint_,
|
||||
})) : undefined,
|
||||
}) : undefined,
|
||||
|
||||
poll: note.hasPoll ? this.populatePoll(note, meId) : undefined,
|
||||
|
||||
|
@ -484,6 +499,100 @@ export class NoteEntityService implements OnModuleInit {
|
|||
return packed;
|
||||
}
|
||||
|
||||
public async packDeletedNote(
|
||||
srcId: MiNote['id'] | MiDeletedNote,
|
||||
me?: { id: MiUser['id'] } | null | undefined,
|
||||
options?: {
|
||||
detail?: boolean;
|
||||
skipHide?: boolean;
|
||||
withReactionAndUserPairCache?: boolean;
|
||||
_hint_?: {
|
||||
bufferedReactions: Map<MiNote['id'], { deltas: Record<string, number>; pairs: ([MiUser['id'], string])[] }> | null;
|
||||
myReactions: Map<MiNote['id'], string | null>;
|
||||
packedFiles: Map<MiNote['fileIds'][number], Packed<'DriveFile'> | null>;
|
||||
packedUsers: Map<MiUser['id'], Packed<'UserLite'>>
|
||||
};
|
||||
},
|
||||
): Promise<Packed<'Note'>> {
|
||||
const opts = Object.assign({
|
||||
detail: true,
|
||||
skipHide: false,
|
||||
withReactionAndUserPairCache: false,
|
||||
}, options);
|
||||
|
||||
const deletedNote = typeof srcId === 'object' ? srcId : await this.deletedNotesRepository.findOneOrFail({
|
||||
where: {
|
||||
id: srcId,
|
||||
},
|
||||
relations: ['user', 'renote', 'reply', 'channel'],
|
||||
});
|
||||
|
||||
const packedUsers = options?._hint_?.packedUsers;
|
||||
|
||||
const channel = deletedNote.channelId
|
||||
? deletedNote.channel ?? await this.channelsRepository.findOneBy({ id: deletedNote.channelId })
|
||||
: null;
|
||||
|
||||
return await awaitAll({
|
||||
id: deletedNote.id,
|
||||
createdAt: this.idService.parse(deletedNote.id).date.toISOString(),
|
||||
deletedAt: deletedNote.deletedAt?.toISOString() ?? undefined,
|
||||
userId: deletedNote.userId,
|
||||
user: packedUsers?.get(deletedNote.userId) ?? this.userEntityService.pack(deletedNote.user ?? deletedNote.userId, me),
|
||||
text: deletedNote.deletedAt ? "<small>Deleted note</small>" : "<small>Forgotten remote note. View on remote instance to see contents.</small>",
|
||||
cw: null,
|
||||
visibility: 'public',
|
||||
localOnly: deletedNote.localOnly,
|
||||
reactionAcceptance: 'likeOnly',
|
||||
visibleUserIds: undefined,
|
||||
renoteCount: 0,
|
||||
repliesCount: 0,
|
||||
reactionCount: 0,
|
||||
reactions: {},
|
||||
reactionEmojis: {},
|
||||
reactionAndUserPairCache: [],
|
||||
emojis: {},
|
||||
tags: undefined,
|
||||
fileIds: [],
|
||||
files: [],
|
||||
replyId: deletedNote.replyId,
|
||||
renoteId: deletedNote.renoteId,
|
||||
channelId: deletedNote.channelId ?? undefined,
|
||||
channel: channel ? {
|
||||
id: channel.id,
|
||||
name: channel.name,
|
||||
color: channel.color,
|
||||
isSensitive: channel.isSensitive,
|
||||
allowRenoteToExternal: channel.allowRenoteToExternal,
|
||||
userId: channel.userId,
|
||||
} : undefined,
|
||||
mentions: undefined,
|
||||
hasPoll: undefined,
|
||||
uri: deletedNote.uri ?? undefined,
|
||||
url: deletedNote.url ?? undefined,
|
||||
|
||||
...(opts.detail ? {
|
||||
clippedCount: 0,
|
||||
|
||||
reply: deletedNote.replyId ? this.pack(deletedNote.reply ?? deletedNote.replyId, me, {
|
||||
detail: false,
|
||||
skipHide: opts.skipHide,
|
||||
withReactionAndUserPairCache: opts.withReactionAndUserPairCache,
|
||||
_hint_: options?._hint_,
|
||||
}) : undefined,
|
||||
|
||||
renote: deletedNote.renoteId ? this.pack(deletedNote.renote ?? deletedNote.renoteId, me, {
|
||||
detail: true,
|
||||
skipHide: opts.skipHide,
|
||||
withReactionAndUserPairCache: opts.withReactionAndUserPairCache,
|
||||
_hint_: options?._hint_,
|
||||
}) : undefined,
|
||||
|
||||
poll: undefined,
|
||||
} : {}),
|
||||
});
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async packMany(
|
||||
notes: MiNote[],
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import type { NotesRepository, UsersRepository } from '@/models/_.js';
|
||||
import type { DeletedNotesRepository, NotesRepository, UsersRepository } from '@/models/_.js';
|
||||
import { IdentifiableError } from '@/misc/identifiable-error.js';
|
||||
import type { MiLocalUser, MiRemoteUser, MiUser } from '@/models/User.js';
|
||||
import type { MiNote } from '@/models/Note.js';
|
||||
|
@ -21,6 +21,9 @@ export class GetterService {
|
|||
@Inject(DI.notesRepository)
|
||||
private notesRepository: NotesRepository,
|
||||
|
||||
@Inject(DI.deletedNotesRepository)
|
||||
private deletedNotesRepository: DeletedNotesRepository,
|
||||
|
||||
private userEntityService: UserEntityService,
|
||||
) {
|
||||
}
|
||||
|
@ -39,6 +42,17 @@ export class GetterService {
|
|||
return note;
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async getDeletedNote(noteId: MiNote['id']) {
|
||||
const note = await this.deletedNotesRepository.findOneBy({ id: noteId });
|
||||
|
||||
if (note == null) {
|
||||
throw new IdentifiableError('f2d7e5b8-9d79-4996-b996-89b538a1b71f', 'No such deleted note.');
|
||||
}
|
||||
|
||||
return note;
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async getNoteWithRelations(noteId: MiNote['id']) {
|
||||
const note = await this.notesRepository.findOne({ where: { id: noteId }, relations: ['user', 'reply', 'renote', 'reply.user', 'renote.user'] });
|
||||
|
@ -50,6 +64,17 @@ export class GetterService {
|
|||
return note;
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async getDeletedNoteWithRelations(noteId: MiNote['id']) {
|
||||
const note = await this.deletedNotesRepository.findOne({ where: { id: noteId }, relations: ['user', 'reply', 'renote', 'reply.user', 'renote.user'] });
|
||||
|
||||
if (note == null) {
|
||||
throw new IdentifiableError('f2d7e5b8-9d79-4996-b996-89b538a1b71f', 'No such deleted note.');
|
||||
}
|
||||
|
||||
return note;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user for API processing
|
||||
*/
|
||||
|
|
|
@ -9,6 +9,8 @@ import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
|
|||
import { GetterService } from '@/server/api/GetterService.js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import { MiMeta } from '@/models/Meta.js';
|
||||
import { MiNote } from '@/models/Note.js';
|
||||
import { IdentifiableError } from '@/misc/identifiable-error.js';
|
||||
import { ApiError } from '../../error.js';
|
||||
|
||||
export const meta = {
|
||||
|
@ -55,10 +57,26 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
private getterService: GetterService,
|
||||
) {
|
||||
super(meta, paramDef, async (ps, me) => {
|
||||
const note = await this.getterService.getNoteWithRelations(ps.noteId).catch(err => {
|
||||
if (err.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote);
|
||||
let note: MiNote | void;
|
||||
try {
|
||||
note = await this.getterService.getNoteWithRelations(ps.noteId);
|
||||
} catch (err) {
|
||||
if (err instanceof IdentifiableError && err.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') {
|
||||
try {
|
||||
const deletedNote = await this.getterService.getDeletedNoteWithRelations(ps.noteId);
|
||||
|
||||
return await this.noteEntityService.packDeletedNote(deletedNote, me, {
|
||||
detail: true,
|
||||
});
|
||||
} catch (err) {
|
||||
if (err instanceof IdentifiableError && err.id === 'f2d7e5b8-9d79-4996-b996-89b538a1b71f') {
|
||||
throw new ApiError(meta.errors.noSuchNote);
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
throw err;
|
||||
});
|
||||
}
|
||||
|
||||
if (note.user!.requireSigninToViewContents && me == null) {
|
||||
throw new ApiError(meta.errors.signinRequired);
|
||||
|
|
Loading…
Reference in New Issue