Merge remote-tracking branch 'upstream/develop' into nodeinfostats
This commit is contained in:
commit
326bc0a24f
27 changed files with 179 additions and 148 deletions
|
|
@ -528,44 +528,44 @@ export class NoteEntityService implements OnModuleInit {
|
|||
// パフォーマンスのためノートが作成されてから2秒以上経っていない場合はリアクションを取得しない
|
||||
const oldId = this.idService.gen(Date.now() - 2000);
|
||||
|
||||
const targetNotes: MiNote[] = [];
|
||||
for (const note of notes) {
|
||||
if (isPureRenote(note)) {
|
||||
const reactionsCount = Object.values(this.reactionsBufferingService.mergeReactions(note.renote.reactions, bufferedReactions?.get(note.renote.id)?.deltas ?? {})).reduce((a, b) => a + b, 0);
|
||||
if (reactionsCount === 0) {
|
||||
myReactionsMap.set(note.renote.id, null);
|
||||
} else if (reactionsCount <= note.renote.reactionAndUserPairCache.length + (bufferedReactions?.get(note.renote.id)?.pairs.length ?? 0)) {
|
||||
const pairInBuffer = bufferedReactions?.get(note.renote.id)?.pairs.find(p => p[0] === meId);
|
||||
if (pairInBuffer) {
|
||||
myReactionsMap.set(note.renote.id, pairInBuffer[1]);
|
||||
} else {
|
||||
const pair = note.renote.reactionAndUserPairCache.find(p => p.startsWith(meId));
|
||||
myReactionsMap.set(note.renote.id, pair ? pair.split('/')[1] : null);
|
||||
}
|
||||
} else {
|
||||
idsNeedFetchMyReaction.add(note.renote.id);
|
||||
// we may need to fetch 'my reaction' for renote target.
|
||||
targetNotes.push(note.renote);
|
||||
if (note.renote.reply) {
|
||||
// idem if the renote is also a reply.
|
||||
targetNotes.push(note.renote.reply);
|
||||
}
|
||||
} else if (note.reply) {
|
||||
// idem for OP of a regular reply.
|
||||
targetNotes.push(note.reply);
|
||||
} else {
|
||||
if (note.id < oldId) {
|
||||
const reactionsCount = Object.values(this.reactionsBufferingService.mergeReactions(note.reactions, bufferedReactions?.get(note.id)?.deltas ?? {})).reduce((a, b) => a + b, 0);
|
||||
if (reactionsCount === 0) {
|
||||
myReactionsMap.set(note.id, null);
|
||||
} else if (reactionsCount <= note.reactionAndUserPairCache.length + (bufferedReactions?.get(note.id)?.pairs.length ?? 0)) {
|
||||
const pairInBuffer = bufferedReactions?.get(note.id)?.pairs.find(p => p[0] === meId);
|
||||
if (pairInBuffer) {
|
||||
myReactionsMap.set(note.id, pairInBuffer[1]);
|
||||
} else {
|
||||
const pair = note.reactionAndUserPairCache.find(p => p.startsWith(meId));
|
||||
myReactionsMap.set(note.id, pair ? pair.split('/')[1] : null);
|
||||
}
|
||||
} else {
|
||||
idsNeedFetchMyReaction.add(note.id);
|
||||
}
|
||||
targetNotes.push(note);
|
||||
} else {
|
||||
myReactionsMap.set(note.id, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const note of targetNotes) {
|
||||
const reactionsCount = Object.values(this.reactionsBufferingService.mergeReactions(note.reactions, bufferedReactions?.get(note.id)?.deltas ?? {})).reduce((a, b) => a + b, 0);
|
||||
if (reactionsCount === 0) {
|
||||
myReactionsMap.set(note.id, null);
|
||||
} else if (reactionsCount <= note.reactionAndUserPairCache.length + (bufferedReactions?.get(note.id)?.pairs.length ?? 0)) {
|
||||
const pairInBuffer = bufferedReactions?.get(note.id)?.pairs.find(p => p[0] === meId);
|
||||
if (pairInBuffer) {
|
||||
myReactionsMap.set(note.id, pairInBuffer[1]);
|
||||
} else {
|
||||
const pair = note.reactionAndUserPairCache.find(p => p.startsWith(meId));
|
||||
myReactionsMap.set(note.id, pair ? pair.split('/')[1] : null);
|
||||
}
|
||||
} else {
|
||||
idsNeedFetchMyReaction.add(note.id);
|
||||
}
|
||||
}
|
||||
|
||||
const myReactions = idsNeedFetchMyReaction.size > 0 ? await this.noteReactionsRepository.findBy({
|
||||
userId: meId,
|
||||
noteId: In(Array.from(idsNeedFetchMyReaction)),
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import { bindThis } from '@/decorators.js';
|
|||
import { QueueLoggerService } from '../QueueLoggerService.js';
|
||||
import type * as Bull from 'bullmq';
|
||||
import type { DbUserImportJobData } from '../types.js';
|
||||
import type { Config } from '@/config.js';
|
||||
|
||||
// TODO: 名前衝突時の動作を選べるようにする
|
||||
@Injectable()
|
||||
|
|
@ -24,6 +25,9 @@ export class ImportCustomEmojisProcessorService {
|
|||
private logger: Logger;
|
||||
|
||||
constructor(
|
||||
@Inject(DI.config)
|
||||
private config: Config,
|
||||
|
||||
@Inject(DI.driveFilesRepository)
|
||||
private driveFilesRepository: DriveFilesRepository,
|
||||
|
||||
|
|
@ -57,7 +61,7 @@ export class ImportCustomEmojisProcessorService {
|
|||
|
||||
try {
|
||||
fs.writeFileSync(destPath, '', 'binary');
|
||||
await this.downloadService.downloadUrl(file.url, destPath);
|
||||
await this.downloadService.downloadUrl(file.url, destPath, { operationTimeout: this.config.import?.downloadTimeout, maxSize: this.config.import?.maxFileSize });
|
||||
} catch (e) { // TODO: 何度か再試行
|
||||
if (e instanceof Error || typeof e === 'string') {
|
||||
this.logger.error(e);
|
||||
|
|
|
|||
|
|
@ -626,7 +626,7 @@ export class ImportNotesProcessorService {
|
|||
|
||||
if (!exists) {
|
||||
try {
|
||||
await this.downloadService.downloadUrl(videos[0].url, filePath);
|
||||
await this.downloadUrl(videos[0].url, filePath);
|
||||
} catch (e) { // TODO: 何度か再試行
|
||||
this.logger.error(e instanceof Error ? e : new Error(e as string));
|
||||
}
|
||||
|
|
@ -651,7 +651,7 @@ export class ImportNotesProcessorService {
|
|||
|
||||
if (!exists) {
|
||||
try {
|
||||
await this.downloadService.downloadUrl(file.media_url_https, filePath);
|
||||
await this.downloadUrl(file.media_url_https, filePath);
|
||||
} catch (e) { // TODO: 何度か再試行
|
||||
this.logger.error(e instanceof Error ? e : new Error(e as string));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -68,6 +68,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
for (let i = 0; i < ps.count; i++) {
|
||||
ticketsPromises.push(this.registrationTicketsRepository.insertOne({
|
||||
id: this.idService.gen(),
|
||||
createdBy: me,
|
||||
createdById: me.id,
|
||||
expiresAt: ps.expiresAt ? new Date(ps.expiresAt) : null,
|
||||
code: generateInviteCode(),
|
||||
}));
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import { IsNull } from 'typeorm';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import type { EmojisRepository } from '@/models/_.js';
|
||||
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||
|
|
@ -59,7 +58,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
const emojis = await this.emojisRepository.createQueryBuilder()
|
||||
.where('host IS NULL')
|
||||
.orderBy('LOWER(category)', 'ASC')
|
||||
.orderBy('LOWER(name)', 'ASC')
|
||||
.addOrderBy('LOWER(name)', 'ASC')
|
||||
.getMany();
|
||||
return {
|
||||
emojis: await this.emojiEntityService.packSimpleMany(emojis),
|
||||
|
|
|
|||
|
|
@ -103,11 +103,42 @@ export default abstract class Channel {
|
|||
|
||||
public onMessage?(type: string, body: JsonValue): void;
|
||||
|
||||
public async assignMyReaction(note: Packed<'Note'>, noteEntityService: NoteEntityService) {
|
||||
if (this.user && Object.keys(note.reactions).length > 0) {
|
||||
const myReaction = await noteEntityService.populateMyReaction(note, this.user.id);
|
||||
note.myReaction = myReaction;
|
||||
public async assignMyReaction(note: Packed<'Note'>, noteEntityService: NoteEntityService): Promise<Packed<'Note'>> {
|
||||
let changed = false;
|
||||
// StreamingApiServerService creates a single EventEmitter per server process,
|
||||
// so a new note arriving from redis gets de-serialised once per server process,
|
||||
// and then that single object is passed to all active channels on each connection.
|
||||
// If we didn't clone the notes here, different connections would asynchronously write
|
||||
// different values to the same object, resulting in a random value being sent to each frontend. -- Dakkar
|
||||
const clonedNote = { ...note };
|
||||
if (this.user && isRenotePacked(note) && !isQuotePacked(note)) {
|
||||
if (note.renote && Object.keys(note.renote.reactions).length > 0) {
|
||||
const myReaction = await noteEntityService.populateMyReaction(note.renote, this.user.id);
|
||||
if (myReaction) {
|
||||
changed = true;
|
||||
clonedNote.renote = { ...note.renote };
|
||||
clonedNote.renote.myReaction = myReaction;
|
||||
}
|
||||
}
|
||||
if (note.renote?.reply && Object.keys(note.renote.reply.reactions).length > 0) {
|
||||
const myReaction = await noteEntityService.populateMyReaction(note.renote.reply, this.user.id);
|
||||
if (myReaction) {
|
||||
changed = true;
|
||||
clonedNote.renote = { ...note.renote };
|
||||
clonedNote.renote.reply = { ...note.renote.reply };
|
||||
clonedNote.renote.reply.myReaction = myReaction;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this.user && note.reply && Object.keys(note.reply.reactions).length > 0) {
|
||||
const myReaction = await noteEntityService.populateMyReaction(note.reply, this.user.id);
|
||||
if (myReaction) {
|
||||
changed = true;
|
||||
clonedNote.reply = { ...note.reply };
|
||||
clonedNote.reply.myReaction = myReaction;
|
||||
}
|
||||
}
|
||||
return changed ? clonedNote : note;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -65,24 +65,11 @@ class BubbleTimelineChannel extends Channel {
|
|||
|
||||
if (this.isNoteMutedOrBlocked(note)) return;
|
||||
|
||||
const reactionsToFetch = [];
|
||||
if (this.user && isRenotePacked(note) && !isQuotePacked(note)) {
|
||||
if (note.renote) {
|
||||
reactionsToFetch.push(this.assignMyReaction(note.renote, this.noteEntityService));
|
||||
if (note.renote.reply) {
|
||||
reactionsToFetch.push(this.assignMyReaction(note.renote.reply, this.noteEntityService));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this.user && note.reply) {
|
||||
reactionsToFetch.push(this.assignMyReaction(note.reply, this.noteEntityService));
|
||||
}
|
||||
const clonedNote = await this.assignMyReaction(note, this.noteEntityService);
|
||||
|
||||
await Promise.all(reactionsToFetch);
|
||||
this.connection.cacheNote(clonedNote);
|
||||
|
||||
this.connection.cacheNote(note);
|
||||
|
||||
this.send('note', note);
|
||||
this.send('note', clonedNote);
|
||||
}
|
||||
|
||||
@bindThis
|
||||
|
|
|
|||
|
|
@ -60,24 +60,11 @@ class GlobalTimelineChannel extends Channel {
|
|||
|
||||
if (this.isNoteMutedOrBlocked(note)) return;
|
||||
|
||||
const reactionsToFetch = [];
|
||||
if (this.user && isRenotePacked(note) && !isQuotePacked(note)) {
|
||||
if (note.renote) {
|
||||
reactionsToFetch.push(this.assignMyReaction(note.renote, this.noteEntityService));
|
||||
if (note.renote.reply) {
|
||||
reactionsToFetch.push(this.assignMyReaction(note.renote.reply, this.noteEntityService));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this.user && note.reply) {
|
||||
reactionsToFetch.push(this.assignMyReaction(note.reply, this.noteEntityService));
|
||||
}
|
||||
const clonedNote = await this.assignMyReaction(note, this.noteEntityService);
|
||||
|
||||
await Promise.all(reactionsToFetch);
|
||||
this.connection.cacheNote(clonedNote);
|
||||
|
||||
this.connection.cacheNote(note);
|
||||
|
||||
this.send('note', note);
|
||||
this.send('note', clonedNote);
|
||||
}
|
||||
|
||||
@bindThis
|
||||
|
|
|
|||
|
|
@ -81,24 +81,11 @@ class HomeTimelineChannel extends Channel {
|
|||
|
||||
if (this.isNoteMutedOrBlocked(note)) return;
|
||||
|
||||
const reactionsToFetch = [];
|
||||
if (this.user && isRenotePacked(note) && !isQuotePacked(note)) {
|
||||
if (note.renote) {
|
||||
reactionsToFetch.push(this.assignMyReaction(note.renote, this.noteEntityService));
|
||||
if (note.renote.reply) {
|
||||
reactionsToFetch.push(this.assignMyReaction(note.renote.reply, this.noteEntityService));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this.user && note.reply) {
|
||||
reactionsToFetch.push(this.assignMyReaction(note.reply, this.noteEntityService));
|
||||
}
|
||||
const clonedNote = await this.assignMyReaction(note, this.noteEntityService);
|
||||
|
||||
await Promise.all(reactionsToFetch);
|
||||
this.connection.cacheNote(clonedNote);
|
||||
|
||||
this.connection.cacheNote(note);
|
||||
|
||||
this.send('note', note);
|
||||
this.send('note', clonedNote);
|
||||
}
|
||||
|
||||
@bindThis
|
||||
|
|
|
|||
|
|
@ -98,24 +98,11 @@ class HybridTimelineChannel extends Channel {
|
|||
}
|
||||
}
|
||||
|
||||
const reactionsToFetch = [];
|
||||
if (this.user && isRenotePacked(note) && !isQuotePacked(note)) {
|
||||
if (note.renote) {
|
||||
reactionsToFetch.push(this.assignMyReaction(note.renote, this.noteEntityService));
|
||||
if (note.renote.reply) {
|
||||
reactionsToFetch.push(this.assignMyReaction(note.renote.reply, this.noteEntityService));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this.user && note.reply) {
|
||||
reactionsToFetch.push(this.assignMyReaction(note.reply, this.noteEntityService));
|
||||
}
|
||||
const clonedNote = await this.assignMyReaction(note, this.noteEntityService);
|
||||
|
||||
await Promise.all(reactionsToFetch);
|
||||
this.connection.cacheNote(clonedNote);
|
||||
|
||||
this.connection.cacheNote(note);
|
||||
|
||||
this.send('note', note);
|
||||
this.send('note', clonedNote);
|
||||
}
|
||||
|
||||
@bindThis
|
||||
|
|
|
|||
|
|
@ -70,24 +70,11 @@ class LocalTimelineChannel extends Channel {
|
|||
|
||||
if (this.isNoteMutedOrBlocked(note)) return;
|
||||
|
||||
const reactionsToFetch = [];
|
||||
if (this.user && isRenotePacked(note) && !isQuotePacked(note)) {
|
||||
if (note.renote) {
|
||||
reactionsToFetch.push(this.assignMyReaction(note.renote, this.noteEntityService));
|
||||
if (note.renote.reply) {
|
||||
reactionsToFetch.push(this.assignMyReaction(note.renote.reply, this.noteEntityService));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this.user && note.reply) {
|
||||
reactionsToFetch.push(this.assignMyReaction(note.reply, this.noteEntityService));
|
||||
}
|
||||
const clonedNote = await this.assignMyReaction(note, this.noteEntityService);
|
||||
|
||||
await Promise.all(reactionsToFetch);
|
||||
this.connection.cacheNote(clonedNote);
|
||||
|
||||
this.connection.cacheNote(note);
|
||||
|
||||
this.send('note', note);
|
||||
this.send('note', clonedNote);
|
||||
}
|
||||
|
||||
@bindThis
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue